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.

1472 lines
41 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. evtlog.c
  5. Abstract:
  6. Contains all the routines for supporting cluster wide eventlogging.
  7. Author:
  8. Sunita Shrivastava (sunitas) 24-Apr-1996
  9. Revision History:
  10. --*/
  11. #include "evtlogp.h"
  12. #include "simpleq.h"
  13. #include "nm.h" // to get NmLocalNodeIdString //
  14. #include "dm.h"
  15. #include "clussprt.h"
  16. //since the eventlog replication requires services.exe calling into the
  17. //cluster service
  18. LPWSTR g_pszServicesPath = NULL;
  19. DWORD g_dwServicesPid = 0;
  20. //
  21. // Local data
  22. //
  23. #define OUTGOING_PROPAGATION_ENABLED 0x00000001
  24. //#define INCOMING_PROPAGATION_ENABLED 0x00000002
  25. #define TRACE_EVERYTHING_ENABLED 0x00001000
  26. #define PROPAGATION_ENABLED OUTGOING_PROPAGATION_ENABLED
  27. static WORD LastFailHour = -1;
  28. static WORD LastFailDay = -1;
  29. static BITSET EvpUpNodeSet = 0;
  30. static SIMPLEQUEUE IncomingQueue;
  31. static SIMPLEQUEUE OutgoingQueue;
  32. static CLRTL_WORK_ITEM EvtlogWriterWorkItem;
  33. static CLRTL_WORK_ITEM EvtBroadcasterWorkItem;
  34. static DWORD DefaultNodePropagate = PROPAGATION_ENABLED;
  35. static DWORD DefaultClusterPropagate = PROPAGATION_ENABLED;
  36. #define EVTLOG_DELTA_GENERATION 1
  37. #ifdef EVTLOG_DELTA_GENERATION
  38. static DWORD g_dwGenerateDeltas = 1;
  39. static DWORD g_dwVersionsAllowDeltaGeneration = 0;
  40. static CLRTL_WORK_ITEM EvVersionCalcWorkItem;
  41. INT64 EvtTimeDiff[ClusterMinNodeId + ClusterDefaultMaxNodes];
  42. VOID
  43. EvVersionCalcCb(
  44. IN PCLRTL_WORK_ITEM WorkItem,
  45. IN DWORD Status,
  46. IN DWORD BytesTransferred,
  47. IN ULONG_PTR IoContext
  48. );
  49. VOID
  50. EvpVersionCalc(
  51. OUT LPDWORD pdwAllowDeltaGeneration
  52. );
  53. #endif
  54. #define AsyncEvtlogReplication CLUSTER_MAKE_VERSION(NT5_MAJOR_VERSION,1978)
  55. #define OUTGOING_QUEUE_SIZE (256 * 1024) // Max size of the batched event buffer that can come in from eventlog service
  56. #define OUTGOING_QUEUE_NAME L"System Event Replication Output Queue"
  57. #define INCOMING_QUEUE_SIZE (OUTGOING_QUEUE_SIZE * 3)
  58. #define INCOMING_QUEUE_NAME L"System Event Replication Input Queue"
  59. #define DROPPED_DATA_NOTIFY_INTERVAL (2*60) // in seconds (2mins)
  60. #define CHECK_CLUSTER_REGISTRY_EVERY 10 // seconds
  61. #define EVTLOG_TRACE_EVERYTHING 1
  62. #ifdef EVTLOG_TRACE_EVERYTHING
  63. # define EvtlogPrint(__evtlogtrace__) \
  64. do { if (EventlogTraceEverything) {ClRtlLogPrint __evtlogtrace__;} } while(0)
  65. #else
  66. # define EvtLogPrint(x)
  67. #endif
  68. DWORD EventlogTraceEverything = 1;
  69. RPC_BINDING_HANDLE EvtRpcBindings[ClusterMinNodeId + ClusterDefaultMaxNodes];
  70. BOOLEAN EvInitialized = FALSE;
  71. /////////////// Forward Declarations ////////////////
  72. DWORD
  73. InitializeQueues(
  74. VOID
  75. );
  76. VOID
  77. DestroyQueues(
  78. VOID);
  79. VOID
  80. ReadRegistryKeys(
  81. VOID);
  82. VOID
  83. PeriodicRegistryCheck(
  84. VOID);
  85. ///////////// End of forward Declarations ////////////
  86. /****
  87. @doc EXTERNAL INTERFACES CLUSSVC EVTLOG
  88. ****/
  89. /****
  90. @func DWORD | EvInitialize| This initializes the cluster
  91. wide eventlog replicating services.
  92. @rdesc Returns a result code. ERROR_SUCCESS on success.
  93. @comm
  94. @xref <f EvShutdown>
  95. ****/
  96. DWORD EvInitialize()
  97. {
  98. DWORD i;
  99. WCHAR wServicesName[] = L"services.exe";
  100. WCHAR wCallerModuleName[] = L"\\system32\\";
  101. WCHAR wCallerPath[MAX_PATH + 1];
  102. LPWSTR pszServicesPath;
  103. DWORD dwNumChar;
  104. DWORD dwStatus = ERROR_SUCCESS;
  105. //
  106. // Initialize Per-node information
  107. //
  108. for (i=ClusterMinNodeId; i <= NmMaxNodeId; i++) {
  109. EvtRpcBindings[i] = NULL;
  110. }
  111. #ifdef EVTLOG_DELTA_GENERATION
  112. for (i=ClusterMinNodeId; i <= NmMaxNodeId; i++) {
  113. EvtTimeDiff[i] = 0;
  114. }
  115. #endif
  116. //get the path name for %windir%\system32\services.exe
  117. dwNumChar = GetWindowsDirectoryW(wCallerPath, MAX_PATH);
  118. if(dwNumChar == 0)
  119. {
  120. dwStatus = GetLastError();
  121. goto FnExit;
  122. }
  123. //need to allocate more memory
  124. pszServicesPath = LocalAlloc(LMEM_FIXED, (sizeof(WCHAR) *
  125. (lstrlenW(wCallerPath) + lstrlenW(wCallerModuleName) +
  126. lstrlenW(wServicesName) + 1)));
  127. if (!pszServicesPath)
  128. {
  129. dwStatus = GetLastError();
  130. goto FnExit;
  131. }
  132. lstrcpyW(pszServicesPath, wCallerPath);
  133. lstrcatW(pszServicesPath, wCallerModuleName);
  134. lstrcatW(pszServicesPath, wServicesName);
  135. g_pszServicesPath = pszServicesPath;
  136. EvInitialized = TRUE;
  137. FnExit:
  138. return(dwStatus);
  139. } // EvInitialize
  140. /****
  141. @doc EXTERNAL INTERFACES CLUSSVC EVTLOG
  142. ****/
  143. /****
  144. @func DWORD | EvOnline| This finishes initializing the cluster
  145. wide eventlog replicating services.
  146. @rdesc Returns a result code. ERROR_SUCCESS on success.
  147. @comm This calls ElfrRegisterClusterSvc() and calls EvpPropPendingEvents()
  148. to propagate events logged since the start of the eventlog service.
  149. @xref <f EvShutdown>
  150. ****/
  151. DWORD EvOnline()
  152. {
  153. DWORD dwError=ERROR_SUCCESS;
  154. PPACKEDEVENTINFO pPackedEventInfo=NULL;
  155. DWORD dwEventInfoSize;
  156. DWORD dwSequence;
  157. CLUSTER_NODE_STATE state;
  158. DWORD i;
  159. PNM_NODE node;
  160. ClRtlLogPrint(LOG_NOISE, "[EVT] EvOnline\n");
  161. #ifdef EVTLOG_DELTA_GENERATION
  162. //initialize the work item for version calculations
  163. ClRtlInitializeWorkItem(
  164. &EvVersionCalcWorkItem,
  165. EvVersionCalcCb,
  166. (PVOID) &g_dwVersionsAllowDeltaGeneration
  167. );
  168. //check whether the cluster version allows delta generation
  169. //this needs to be done for readregistrykeys() is invoked
  170. //by InitializeQueues() so that g_dwGenerateDeltas is setappropriately
  171. EvpVersionCalc(&g_dwVersionsAllowDeltaGeneration);
  172. ClRtlLogPrint(LOG_NOISE,
  173. "[EVT] EvOnline : Compiled with Delta generation enabled\n");
  174. #endif
  175. dwError = InitializeQueues();
  176. if (dwError != ERROR_SUCCESS) {
  177. return dwError;
  178. }
  179. //
  180. // Register for node up/down events.
  181. //
  182. dwError = EpRegisterEventHandler(
  183. (CLUSTER_EVENT_NODE_UP | CLUSTER_EVENT_NODE_DOWN_EX |
  184. CLUSTER_EVENT_NODE_ADDED | CLUSTER_EVENT_NODE_DELETED),
  185. EvpClusterEventHandler
  186. );
  187. if (dwError != ERROR_SUCCESS) {
  188. ClRtlLogPrint(LOG_NOISE,
  189. "[EVT] EvOnline : Failed to register for cluster events, status %1!u!\n",
  190. dwError);
  191. return(dwError);
  192. }
  193. // Initialize Per-node information
  194. //
  195. for (i=ClusterMinNodeId; i <= NmMaxNodeId; i++)
  196. {
  197. if (i != NmLocalNodeId) {
  198. node = NmReferenceNodeById(i);
  199. if (node != NULL) {
  200. DWORD version = NmGetNodeHighestVersion(node);
  201. state = NmGetNodeState(node);
  202. if ( (state == ClusterNodeUp) ||
  203. (state == ClusterNodePaused)
  204. )
  205. {
  206. if (version >= AsyncEvtlogReplication) {
  207. BitsetAdd(EvpUpNodeSet, i);
  208. ClRtlLogPrint(LOG_NOISE,
  209. "[EVT] Node up: %1!u!, new UpNodeSet: %2!04x!\n",
  210. i,
  211. EvpUpNodeSet
  212. );
  213. } else {
  214. ClRtlLogPrint(LOG_NOISE,
  215. "[EVT] Evtlog replication is not allowed for node %1!u! (version %2!x!)\n",
  216. i,
  217. version
  218. );
  219. }
  220. }
  221. OmDereferenceObject(node);
  222. }
  223. }
  224. }
  225. //TODO :: SS - currently the eventlog propagation api
  226. //has been added to clusapi. In future, if we need
  227. //to define a general purpose interface for communication
  228. //with other services on the same system, then we need
  229. //to register and advertize that interface here.
  230. //call the event logger to get routines that have been logged so far.
  231. ClRtlLogPrint(LOG_NOISE, "[EVT] EvOnline : calling ElfRegisterClusterSvc\n");
  232. dwError = ElfRegisterClusterSvc(NULL, &dwEventInfoSize, &pPackedEventInfo);
  233. if (dwError != ERROR_SUCCESS)
  234. {
  235. ClRtlLogPrint(LOG_CRITICAL,
  236. "[EVT] EvOnline : ElfRegisterClusterSvc returned %1!u!\n",
  237. dwError);
  238. return(dwError);
  239. }
  240. //post them to other nodes in the cluster
  241. if (pPackedEventInfo && dwEventInfoSize)
  242. {
  243. ClRtlLogPrint(LOG_NOISE,
  244. "[EVT] EvOnline: pPackedEventInfo->ulSize=%1!d! pPackedEventInfo->ulNulEventsForLogFile=%2!d!\r\n",
  245. pPackedEventInfo->ulSize, pPackedEventInfo->ulNumEventsForLogFile);
  246. EvpPropPendingEvents(dwEventInfoSize, pPackedEventInfo);
  247. MIDL_user_free ( pPackedEventInfo );
  248. }
  249. return (dwError);
  250. }
  251. /****
  252. @func DWORD | EvCreateRpcBindings| This creates an RPC binding
  253. for a specified node.
  254. @rdesc Returns a result code. ERROR_SUCCESS on success.
  255. @comm
  256. @xref
  257. ****/
  258. DWORD
  259. EvCreateRpcBindings(
  260. PNM_NODE Node
  261. )
  262. {
  263. DWORD Status;
  264. RPC_BINDING_HANDLE BindingHandle;
  265. CL_NODE_ID NodeId = NmGetNodeId(Node);
  266. ClRtlLogPrint(LOG_NOISE,
  267. "[EVT] Creating RPC bindings for node %1!u!.\n",
  268. NodeId
  269. );
  270. //
  271. // Main binding
  272. //
  273. if (EvtRpcBindings[NodeId] != NULL) {
  274. //
  275. // Reuse the old binding.
  276. //
  277. Status = ClMsgVerifyRpcBinding(EvtRpcBindings[NodeId]);
  278. if (Status != ERROR_SUCCESS) {
  279. ClRtlLogPrint(LOG_ERROR,
  280. "[EVT] Failed to verify 1st RPC binding for node %1!u!, status %2!u!.\n",
  281. NodeId,
  282. Status
  283. );
  284. return(Status);
  285. }
  286. }
  287. else {
  288. //
  289. // Create a new binding
  290. //
  291. Status = ClMsgCreateRpcBinding(
  292. Node,
  293. &(EvtRpcBindings[NodeId]),
  294. 0 );
  295. if (Status != ERROR_SUCCESS) {
  296. ClRtlLogPrint(LOG_ERROR,
  297. "[EVT] Failed to create 1st RPC binding for node %1!u!, status %2!u!.\n",
  298. NodeId,
  299. Status
  300. );
  301. return(Status);
  302. }
  303. }
  304. return(ERROR_SUCCESS);
  305. } // EvCreateRpcBindings
  306. /****
  307. @func DWORD | EvShutdown| This deinitializes the cluster
  308. wide eventlog replication services.
  309. @rdesc Returns a result code. ERROR_SUCCESS on success.
  310. @comm The cluster register deregisters with the eventlog service.
  311. @xref <f EvInitialize>
  312. ****/
  313. DWORD EvShutdown(void)
  314. {
  315. DWORD dwError=ERROR_SUCCESS;
  316. if (EvInitialized) {
  317. PPACKEDEVENTINFO pPackedEventInfo;
  318. DWORD dwEventInfoSize;
  319. DWORD i;
  320. ClRtlLogPrint(LOG_NOISE,
  321. "[EVT] EvShutdown\r\n");
  322. //call the event logger to get routines that have been logged so far.
  323. ElfDeregisterClusterSvc(NULL);
  324. DestroyQueues();
  325. // TODO [GorN 9/23/1999]
  326. // When DestroyQueues starts doing what it is supposed to do,
  327. // (i.e. flush/wait/destroy), enable the code below
  328. #if 0
  329. //
  330. // Free per-node information
  331. //
  332. for (i=ClusterMinNodeId; i <= NmMaxNodeId; i++) {
  333. if (EvtRpcBindings[i] != NULL) {
  334. ClMsgDeleteRpcBinding(EvtRpcBindings[i]);
  335. EvtRpcBindings[i] = NULL;
  336. }
  337. }
  338. #endif
  339. }
  340. return (dwError);
  341. }
  342. /****
  343. @func DWORD | EvpClusterEventHandler| Handler for internal cluster
  344. events.
  345. @rdesc Returns a result code. ERROR_SUCCESS on success.
  346. @comm
  347. @xref <f EvInitialize>
  348. ****/
  349. DWORD
  350. EvpClusterEventHandler(
  351. IN CLUSTER_EVENT Event,
  352. IN PVOID Context
  353. )
  354. {
  355. DWORD NodeId;
  356. switch(Event)
  357. {
  358. #ifdef EVTLOG_DELTA_GENERATION
  359. case CLUSTER_EVENT_NODE_DELETED:
  360. case CLUSTER_EVENT_NODE_ADDED:
  361. {
  362. //post a work item to delayed thread to calculate the versions
  363. //if it is less than whistler,
  364. ClRtlPostItemWorkQueue(CsDelayedWorkQueue, &EvVersionCalcWorkItem, 0, 0);
  365. }
  366. break;
  367. #endif
  368. case CLUSTER_EVENT_NODE_UP:
  369. {
  370. PNM_NODE node = (PNM_NODE) Context;
  371. CL_NODE_ID nodeId = NmGetNodeId(node);
  372. DWORD version = NmGetNodeHighestVersion(node);
  373. if ( version >= AsyncEvtlogReplication )
  374. {
  375. BitsetAdd(EvpUpNodeSet, nodeId);
  376. ClRtlLogPrint(LOG_NOISE,
  377. "[EVT] Node up: %1!u!, new UpNodeSet: %2!04x!\n",
  378. nodeId,
  379. EvpUpNodeSet
  380. );
  381. } else {
  382. ClRtlLogPrint(LOG_NOISE,
  383. "[EVT] Evtlog replication is not allowed for node %1!u! (version %2!x!)\n",
  384. nodeId,
  385. version
  386. );
  387. }
  388. }
  389. break;
  390. case CLUSTER_EVENT_NODE_DOWN_EX:
  391. {
  392. BITSET downedNodes = (BITSET)((ULONG_PTR)Context);
  393. BitsetSubtract(EvpUpNodeSet, downedNodes);
  394. ClRtlLogPrint(LOG_NOISE,
  395. "[EVT] Nodes down: %1!04X!, new UpNodeSet: %2!04x!\n",
  396. downedNodes,
  397. EvpUpNodeSet
  398. );
  399. }
  400. break;
  401. default:
  402. break;
  403. }
  404. return(ERROR_SUCCESS);
  405. }
  406. /****
  407. @func DWORD | s_EvPropEvents| This is the server entry point for
  408. receiving eventlog information from other nodes of the cluster
  409. and logging them locally.
  410. @parm IN handle_t | IDL_handle | The rpc binding handle. Unused.
  411. @parm IN DWORD | dwEventInfoSize | the size of the packed event info structure.
  412. @parm IN UCHAR | *pBuffer| A pointer to the packed
  413. eventinfo structure.
  414. @rdesc returns ERROR_SUCCESS if successful else returns the error code.
  415. @comm This function calls ElfWriteClusterEvents() to log the event propagted
  416. from another node.
  417. @xref
  418. ****/
  419. DWORD
  420. s_EvPropEvents(
  421. IN handle_t IDL_handle,
  422. IN DWORD dwEventInfoSize,
  423. IN UCHAR *pBuffer
  424. )
  425. {
  426. PUCHAR end = pBuffer + dwEventInfoSize;
  427. //should not come here at all
  428. //DebugBreak();
  429. if ( dwEventInfoSize >= sizeof(DWORD) && dwEventInfoSize == (*(PDWORD)pBuffer)) {
  430. ClRtlLogPrint(LOG_UNUSUAL,
  431. "[EVT] Improperly formed packet received of size %1!u!.\n",
  432. dwEventInfoSize
  433. );
  434. return ERROR_SUCCESS;
  435. }
  436. /*
  437. ClRtlLogPrint(LOG_NOISE,
  438. "[EVT] s_EvPropEvents. dwEventInfoSize=%1!d!\r\n",
  439. dwEventInfoSize);
  440. */
  441. #if CLUSTER_BETA
  442. EvtlogPrint((LOG_NOISE, "[EVT] s_EvPropEvents. dwEventInfoSize=%1!d!\r\n",
  443. dwEventInfoSize));
  444. #endif
  445. while (pBuffer < end) {
  446. BOOL success;
  447. success = SimpleQueueTryAdd(&IncomingQueue, SQB_PAYLOADSIZE(pBuffer), SQB_PAYLOAD(pBuffer));
  448. if ( !success ) {
  449. EvtlogPrint((LOG_NOISE, "[EVT] s_EvPropEvents. Put(IncomingQ,%1!d!) failed. empty=%2!d!\n",
  450. SQB_PAYLOADSIZE(pBuffer), IncomingQueue.Empty) );
  451. }
  452. pBuffer = SQB_NEXTBLOCK(pBuffer);
  453. }
  454. return(ERROR_SUCCESS);
  455. }
  456. DWORD
  457. s_EvPropEvents2(
  458. IN handle_t IDL_handle,
  459. IN DWORD dwEventInfoSize,
  460. IN UCHAR *pBuffer,
  461. IN FILETIME ftSendTime,
  462. IN DWORD dwSenderNodeId
  463. )
  464. {
  465. PUCHAR end = pBuffer + dwEventInfoSize;
  466. FILETIME ftReceiptTime;
  467. INT64 iTimeDiff;
  468. ULARGE_INTEGER uliReceiptTime;
  469. ULARGE_INTEGER uliSendTime;
  470. //SS: reliability teams wants the ignoredelta to be about 5 secs
  471. const INT64 iIgnoreDelta = Int32x32To64(5000 , ( 1000 * 10)) ;//5000 msecs(5secs) expressed as 100 nanoseconds
  472. PNM_NODE pNmNode;
  473. LPCWSTR pszSenderNodeName;
  474. WCHAR szNodeId[16];
  475. if ( dwEventInfoSize >= sizeof(DWORD) && dwEventInfoSize == (*(PDWORD)pBuffer)) {
  476. ClRtlLogPrint(LOG_UNUSUAL,
  477. "[EVT] Improperly formed packet received of size %1!u!.\n",
  478. dwEventInfoSize
  479. );
  480. return ERROR_SUCCESS;
  481. }
  482. //received an event, need a time stamp
  483. GetSystemTimeAsFileTime(&ftReceiptTime);
  484. //convert filetimes to large integers
  485. uliReceiptTime.LowPart = ftReceiptTime.dwLowDateTime;
  486. uliReceiptTime.HighPart = ftReceiptTime.dwHighDateTime;
  487. uliSendTime.LowPart = ftSendTime.dwLowDateTime;
  488. uliSendTime.HighPart = ftSendTime.dwHighDateTime;
  489. iTimeDiff = uliReceiptTime.QuadPart - uliSendTime.QuadPart;
  490. wsprintf(szNodeId, L"%u", dwSenderNodeId);
  491. /*
  492. ClRtlLogPrint(LOG_NOISE,
  493. "[EVT] s_EvPropEvents2. dwSenderNodeId=%1!u! pszSenderNodeId = %2!ws!\n",
  494. dwSenderNodeId, szNodeId);
  495. */
  496. //validate sender node id to see it doesnt cause an av!
  497. //and get the name of the sender machine
  498. pNmNode = OmReferenceObjectById(ObjectTypeNode, szNodeId);
  499. if (pNmNode)
  500. {
  501. pszSenderNodeName = OmObjectName(pNmNode);
  502. //compare with last time diff from this node
  503. //use the abs functions for 64 bit integers
  504. if (_abs64(EvtTimeDiff[dwSenderNodeId] - iTimeDiff) > iIgnoreDelta)
  505. {
  506. WCHAR szTimeDiff[64];
  507. //we need to write the deltas or the time diffs into the eventlog
  508. //if we have a stream d1, e1, e2, e3, d2, e4, e5 where d are time diffs
  509. //and e are propagated events, ideally we would like to write them in order
  510. //in this eventlog.
  511. //Alternatives
  512. //a)
  513. //Write it right here and let the events be lazily written by csdelayed worker
  514. //queue threads
  515. //this might appear as d1, e1, d2, e3, e2, e4, e5
  516. //or as d1, d2, e1, e2, e3, e4, e5
  517. //or as d1, d2, e1, e5, e3, e4, e2
  518. //UGH...UGH..worse still this batch can contain events from different
  519. //logs..for each of those the delta needs to go into the corresponding log
  520. //and only once too
  521. //That will require us to grovel through the simple queue payload structures,
  522. //dig inside the eventlog structure and find the logs we should put the delta
  523. //into
  524. //We dont have a handle to all the logs to write into them, that would have
  525. //to change as well - if dont write them into all appropriate logs then the general
  526. //usefulness of this stupid feature is further comprimised, in the sense
  527. //that it really cant be used for corelating events other than the ones
  528. //in the system log and even that would be incorrect.
  529. //Writing them into all logs means cluster service needs
  530. //to register against multiple logs as event source - with
  531. //different names(event log wouldnt like it with the same name)
  532. //and that is ugly as hell as well
  533. _i64tow(iTimeDiff, szTimeDiff, 10);
  534. /*
  535. ClRtlLogPrint(LOG_NOISE,
  536. "[EVT] s_EvPropEvents2. Logging Delta %1!ws!",
  537. szTimeDiff);
  538. */
  539. CL_ASSERT( EVT_EVENT_TIME_DELTA_INFORMATION == CLUSSPRT_EVENT_TIME_DELTA_INFORMATION );
  540. CsLogEvent3(LOG_NOISE, EVT_EVENT_TIME_DELTA_INFORMATION, OmObjectName(NmLocalNode),
  541. pszSenderNodeName, szTimeDiff);
  542. //b)
  543. // Call the eventlog to format the event but dont put it into the eventlog
  544. // but simply insert that into the event queue
  545. // If we dont change the simplequeuetryadd logic and the csdelayed worker queue
  546. // processing, the above stream may appear as d2, e1, d1, e3, e2, e4, e5
  547. // That dont make any sense but then nobody else seems to care about correctness
  548. // STRANGE WORLD !!!
  549. //save the last time logged
  550. EvtTimeDiff[dwSenderNodeId] = iTimeDiff;
  551. //c)Ideal -
  552. //change all the simple queue stuff to make it handle different payload types
  553. //batch events at the source(which is where they are generated) and propagate
  554. //them asynchronously and then simply write them in order as they arrive
  555. //SimpleQ is simply the most worst suited abstraction for event
  556. //log propagation - it consumes space and yet causes lots of
  557. //events to be dropped.
  558. }
  559. else
  560. {
  561. //ClRtlLogPrint(LOG_NOISE,
  562. // "[EVT] s_EvPropEvents2. Delta was too small to log\n");
  563. }
  564. OmDereferenceObject(pNmNode);
  565. }
  566. #if CLUSTER_BETA
  567. EvtlogPrint((LOG_NOISE, "[EVT] s_EvPropEvents2. dwEventInfoSize=%1!d!\r\n",
  568. dwEventInfoSize));
  569. #endif
  570. while (pBuffer < end) {
  571. BOOL success;
  572. success = SimpleQueueTryAdd(&IncomingQueue, SQB_PAYLOADSIZE(pBuffer), SQB_PAYLOAD(pBuffer));
  573. if ( !success ) {
  574. EvtlogPrint((LOG_NOISE, "[EVT] s_EvPropEvents2. Put(IncomingQ,%1!d!) failed. empty=%2!d!\n",
  575. SQB_PAYLOADSIZE(pBuffer), IncomingQueue.Empty) );
  576. }
  577. pBuffer = SQB_NEXTBLOCK(pBuffer);
  578. }
  579. return(ERROR_SUCCESS);
  580. }
  581. /****
  582. @func DWORD | EvpPropPendingEvents| This is called to propagate all the pending
  583. events since the start of the system. And then to propagate any events
  584. generated during the life of the cluster.
  585. @parm IN DWORD | dwEventInfoSize | the size of the packed event info structure.
  586. @parm IN PPACKEDEVENTINFO | pPackedEventInfo| A pointer to the packed
  587. eventinfo structure.
  588. @rdesc returns ERROR_SUCCESS if successful else returns the error code.
  589. @comm This function is called during initialization when a cluster is being formed.
  590. @xref
  591. ****/
  592. DWORD EvpPropPendingEvents(
  593. IN DWORD dwEventInfoSize,
  594. IN PPACKEDEVENTINFO pPackedEventInfo)
  595. {
  596. BOOL success;
  597. success = SimpleQueueTryAdd(&OutgoingQueue, dwEventInfoSize, pPackedEventInfo);
  598. if ( !success ) {
  599. EvtlogPrint((LOG_NOISE, "[EVT] EvpPropPendingEvents: Put(OutgoingQ,%1!d!) failed. empty=%2!d!\n",
  600. dwEventInfoSize, OutgoingQueue.Empty));
  601. }
  602. return ERROR_SUCCESS;
  603. }
  604. /****
  605. @func DWORD | s_ApiEvPropEvents | This is called to propagate eventlogs from
  606. the local system to all other nodes of the cluster.
  607. @parm handle_t | IDL_handle | Not used.
  608. @parm DWORD | dwEventInfoSize | The number of bytes in the following structure.
  609. @parm UCHAR * | pPackedEventInfo | Pointer to a byte structure containing the
  610. PACKEDEVENTINFO structure.
  611. @rdesc Returns ERROR_SUCCESS if successfully propagated events,
  612. else returns the error code.
  613. @comm Currently this function is called for every eventlogged by the eventlog
  614. service. Only the processes running in the SYSTEM account can call this
  615. function.
  616. @xref
  617. ****/
  618. error_status_t
  619. s_ApiEvPropEvents(
  620. IN handle_t IDL_handle,
  621. IN DWORD dwEventInfoSize,
  622. IN UCHAR *pPackedEventInfo
  623. )
  624. {
  625. DWORD dwError = ERROR_SUCCESS;
  626. BOOL bIsLocalSystemAccount;
  627. #if 0
  628. //
  629. // Chittur Subbaraman (chitturs) - 11/7/1999
  630. //
  631. // Modify this function to use ClRtlIsCallerAccountLocalSystemAccount
  632. // instead of GetUserName which
  633. // (1) used to hang in security audit enabled systems if security
  634. // audit log attempts to write to the event log at the time we
  635. // made that API call since that API and the security audit log
  636. // are mutually exclusive for some portions, and
  637. // (2) wrongly checked for an unlocalizable output value "SYSTEM"
  638. // from that API in order to grant access to the client.
  639. //
  640. //
  641. // Impersonate the client.
  642. //
  643. if ( ( dwError = RpcImpersonateClient( IDL_handle ) ) != RPC_S_OK )
  644. {
  645. ClRtlLogPrint( LOG_ERROR,
  646. "[EVT] s_ApiEvPropEvents: Error %1!d! trying to impersonate caller...\n",
  647. dwError
  648. );
  649. goto FnExit;
  650. }
  651. //
  652. // Check that the caller's account is local system account
  653. //
  654. if ( ( dwError = ClRtlIsCallerAccountLocalSystemAccount(
  655. &bIsLocalSystemAccount ) != ERROR_SUCCESS ) )
  656. {
  657. RpcRevertToSelf();
  658. ClRtlLogPrint( LOG_ERROR,
  659. "[EVT] s_ApiEvPropEvents: Error %1!d! trying to check caller's account...\n",
  660. dwError);
  661. goto FnExit;
  662. }
  663. if ( !bIsLocalSystemAccount )
  664. {
  665. RpcRevertToSelf();
  666. dwError = ERROR_ACCESS_DENIED;
  667. ClRtlLogPrint( LOG_ERROR,
  668. "[EVT] s_ApiEvPropEvents: Caller's account is not local system account, denying access...\n");
  669. goto FnExit;
  670. }
  671. RpcRevertToSelf();
  672. #endif
  673. //
  674. // All security checks have passed. Drop the eventlog info into
  675. // the queue.
  676. //
  677. if ( dwEventInfoSize && pPackedEventInfo )
  678. {
  679. dwError = EvpPropPendingEvents( dwEventInfoSize,
  680. ( PPACKEDEVENTINFO ) pPackedEventInfo );
  681. }
  682. return( dwError );
  683. }
  684. VOID
  685. EvtlogWriter(
  686. IN PCLRTL_WORK_ITEM WorkItem,
  687. IN DWORD Status,
  688. IN DWORD BytesTransferred,
  689. IN ULONG_PTR IoContext
  690. )
  691. /*++
  692. Routine Description:
  693. This work item reads events from the
  694. incoming queue and writes them to EventLog service
  695. Arguments:
  696. Not used.
  697. Return Value:
  698. None
  699. --*/
  700. {
  701. PVOID begin, end;
  702. SYSTEMTIME localTime;
  703. DWORD eventsWritten = 0;
  704. #if CLUSTER_BETA
  705. EvtlogPrint( (LOG_NOISE, "[EVT] EvtlogWriter Work Item fired.\n") );
  706. #endif
  707. do {
  708. DWORD dwError;
  709. if ( !SimpleQueueReadOne(&IncomingQueue, &begin, &end) )
  710. {
  711. break;
  712. }
  713. #if CLUSTER_BETA
  714. EvtlogPrint( (LOG_NOISE, "[EVT] EvtlogWriter got %1!d!.\n",
  715. (PUCHAR)end - (PUCHAR)begin ) );
  716. #endif
  717. dwError = ElfWriteClusterEvents(
  718. NULL,
  719. SQB_PAYLOADSIZE(begin),
  720. (PPACKEDEVENTINFO)SQB_PAYLOAD(begin) );
  721. if ( dwError != ERROR_SUCCESS ) {
  722. GetLocalTime( &localTime );
  723. // LastFailHour is initialized to -1, which should not equal any wHour!
  724. // LastFailDay is initialized to -1, which should not equal any wDay!
  725. if ( (LastFailHour != localTime.wHour) || (LastFailDay != localTime.wDay) ) {
  726. LastFailHour = localTime.wHour;
  727. LastFailDay = localTime.wDay;
  728. ClRtlLogPrint(LOG_UNUSUAL,
  729. "[EVT] ElfWriteClusterEvents failed: status = %1!u!\n",
  730. dwError);
  731. }
  732. }
  733. PeriodicRegistryCheck();
  734. } while ( SimpleQueueReadComplete(&IncomingQueue, end) );
  735. #if CLUSTER_BETA
  736. EvtlogPrint( (LOG_NOISE, "[EVT] EvtlogWriter: done.\n" ) );
  737. #endif
  738. if ( eventsWritten > 0 ) {
  739. EvtlogPrint( (LOG_NOISE, "[EVT] EvtlogWriter: wrote %u events to system event log.\n", eventsWritten ) );
  740. }
  741. CheckForDroppedData(&IncomingQueue, FALSE);
  742. }
  743. #ifdef EVTLOG_DELTA_GENERATION
  744. VOID
  745. EvpVersionCalc(
  746. OUT LPDWORD pdwAllowDeltaGeneration
  747. )
  748. /*++
  749. Routine Description:
  750. This work item calculates the cluster versions and based
  751. on that returns whether delta generation can be enabled.
  752. Arguments:
  753. Not used.
  754. Return Value:
  755. None
  756. --*/
  757. {
  758. DWORD dwClusterHighestVersion;
  759. NmGetClusterOperationalVersion(&dwClusterHighestVersion, NULL, NULL);
  760. if (CLUSTER_GET_MAJOR_VERSION(dwClusterHighestVersion) >= NT51_MAJOR_VERSION)
  761. {
  762. *pdwAllowDeltaGeneration = TRUE;
  763. ClRtlLogPrint(LOG_NOISE,
  764. "[EVT] EvpVersionCalc: Delta generation allowed.\n");
  765. }
  766. else
  767. {
  768. *pdwAllowDeltaGeneration = FALSE;
  769. ClRtlLogPrint(LOG_NOISE,
  770. "[EVT] EvpVersionCalc: Delta generation NOT allowed\n");
  771. }
  772. }
  773. VOID
  774. EvVersionCalcCb(
  775. IN PCLRTL_WORK_ITEM WorkItem,
  776. IN DWORD Status,
  777. IN DWORD BytesTransferred,
  778. IN ULONG_PTR IoContext
  779. )
  780. /*++
  781. Routine Description:
  782. This work item calculates the cluster versions on
  783. node up and node down notifications.
  784. Arguments:
  785. Not used.
  786. Return Value:
  787. None
  788. --*/
  789. {
  790. EvpVersionCalc(WorkItem->Context);
  791. }
  792. #endif
  793. VOID
  794. EvtBroadcaster(
  795. IN PCLRTL_WORK_ITEM WorkItem,
  796. IN DWORD Status,
  797. IN DWORD BytesTransferred,
  798. IN ULONG_PTR IoContext
  799. )
  800. /*++
  801. Routine Description:
  802. This work item reads events from the
  803. outgoing queue and RPCs them to all active nodes
  804. Arguments:
  805. Not used.
  806. Return Value:
  807. None
  808. --*/
  809. {
  810. PVOID begin, end;
  811. #if CLUSTER_BETA
  812. EvtlogPrint( (LOG_NOISE, "[EVT] EvtBroadcaster Work Item fired.\n") );
  813. #endif
  814. do {
  815. DWORD i;
  816. if( !SimpleQueueReadAll(&OutgoingQueue, &begin, &end) )
  817. {
  818. EvtlogPrint( (LOG_NOISE, "[EVT] EvtBroadcaster SimplQ read failed.\n") );
  819. break;
  820. }
  821. #if CLUSTER_BETA
  822. EvtlogPrint((LOG_NOISE, "[EVT] EvtBroadcaster got %1!d!.\n",
  823. (PUCHAR)end - (PUCHAR)begin ) );
  824. #endif
  825. #ifdef EVTLOG_DELTA_GENERATION
  826. {
  827. FILETIME ftSendTime;
  828. for (i=ClusterMinNodeId; i <= NmMaxNodeId; i++)
  829. {
  830. if (BitsetIsMember(i, EvpUpNodeSet) && (i != NmLocalNodeId))
  831. {
  832. DWORD dwError;
  833. CL_ASSERT(EvtRpcBindings[i] != NULL);
  834. NmStartRpc(i);
  835. if (g_dwGenerateDeltas)
  836. {
  837. //ClRtlLogPrint(LOG_NOISE,
  838. // "[EVT] EvtBroadcaster(delta) calling EvPropEvents2\n");
  839. GetSystemTimeAsFileTime(&ftSendTime);
  840. dwError = EvPropEvents2(EvtRpcBindings[i],
  841. (DWORD)((PUCHAR)end - (PUCHAR)begin),
  842. (PBYTE)begin,
  843. ftSendTime,
  844. NmLocalNodeId
  845. );
  846. }
  847. else
  848. {
  849. //ClRtlLogPrint(LOG_NOISE,
  850. // "[EVT] EvtBroadcaster(delta) calling EvPropEvents\n");
  851. dwError = EvPropEvents(EvtRpcBindings[i],
  852. (DWORD)((PUCHAR)end - (PUCHAR)begin),
  853. (PBYTE)begin
  854. );
  855. }
  856. NmEndRpc(i);
  857. if ( dwError != ERROR_SUCCESS ) {
  858. ClRtlLogPrint(LOG_UNUSUAL,
  859. "[EVT] EvtBroadcaster: EvPropEvents for node %1!u! "
  860. "failed. status %2!u!\n",
  861. i,
  862. dwError);
  863. NmDumpRpcExtErrorInfo(dwError);
  864. }
  865. }
  866. }
  867. }
  868. #else
  869. for (i=ClusterMinNodeId; i <= NmMaxNodeId; i++)
  870. {
  871. if (BitsetIsMember(i, EvpUpNodeSet) && (i != NmLocalNodeId))
  872. {
  873. DWORD dwError;
  874. CL_ASSERT(EvtRpcBindings[i] != NULL);
  875. NmStartRpc(i);
  876. //ClRtlLogPrint(LOG_NOISE,
  877. // "[EVT] EvtBroadcaster(delta) calling EvPropEvents"\n);
  878. dwError = EvPropEvents(EvtRpcBindings[i],
  879. (DWORD)((PUCHAR)end - (PUCHAR)begin),
  880. (PBYTE)begin
  881. );
  882. NmEndRpc(i);
  883. if ( dwError != ERROR_SUCCESS ) {
  884. ClRtlLogPrint(LOG_UNUSUAL,
  885. "[EVT] Evtbroadcaster: EvPropEvents for node %1!u! "
  886. "failed. status %2!u!\n",
  887. i,
  888. dwError);
  889. NmDumpRpcExtErrorInfo(dwError);
  890. }
  891. }
  892. }
  893. #endif
  894. PeriodicRegistryCheck();
  895. } while ( SimpleQueueReadComplete(&OutgoingQueue, end) );
  896. #if CLUSTER_BETA
  897. EvtlogPrint( (LOG_NOISE, "[EVT] EvtBroadcaster: done.\n" ) );
  898. #endif
  899. CheckForDroppedData(&OutgoingQueue, FALSE);
  900. }
  901. VOID
  902. OutgoingQueueDataAvailable(
  903. IN PSIMPLEQUEUE q
  904. )
  905. /*++
  906. Routine Description:
  907. This routine is called by the queue to notify
  908. that there are data in the queue available for processing
  909. Arguments:
  910. q - which queue has data
  911. Return Value:
  912. None
  913. --*/
  914. {
  915. DWORD status = ClRtlPostItemWorkQueue(
  916. CsDelayedWorkQueue,
  917. &EvtBroadcasterWorkItem,
  918. 0,
  919. 0
  920. );
  921. if (status != ERROR_SUCCESS) {
  922. ClRtlLogPrint(LOG_CRITICAL,
  923. "[EVT] OutgoingQueueDataAvailable, PostWorkItem failed, error %1!u! !\n",
  924. status);
  925. }
  926. }
  927. VOID
  928. IncomingQueueDataAvailable(
  929. IN PSIMPLEQUEUE q
  930. )
  931. /*++
  932. Routine Description:
  933. This routine is called by the queue to notify
  934. that there are data in the queue available for processing
  935. Arguments:
  936. q - which queue has data
  937. Return Value:
  938. None
  939. --*/
  940. {
  941. DWORD status = ClRtlPostItemWorkQueue(
  942. CsDelayedWorkQueue,
  943. &EvtlogWriterWorkItem,
  944. 0,
  945. 0
  946. );
  947. if (status != ERROR_SUCCESS) {
  948. ClRtlLogPrint(LOG_CRITICAL,
  949. "[EVT] IncomingQueueDataAvailable, PostWorkItem failed, error %1!u! !\n",
  950. status);
  951. }
  952. }
  953. VOID
  954. DroppedDataNotify(
  955. IN PWCHAR QueueName,
  956. IN DWORD DroppedDataCount,
  957. IN DWORD DroppedDataSize
  958. )
  959. /*++
  960. Routine Description:
  961. This routine is called by the queue to notify
  962. that some data were lost because the queue was full
  963. Arguments:
  964. QueueName - Queue Name
  965. DataCount - How many chunks of data were lost
  966. DataSize - Total size fo the lost data
  967. Return Value:
  968. None
  969. --*/
  970. {
  971. WCHAR count[32];
  972. WCHAR size[32];
  973. ClRtlLogPrint(LOG_UNUSUAL,
  974. "[EVT] %1!ws!: dropped %2!d!, total dropped size %3!d!.\n",
  975. QueueName,
  976. DroppedDataCount,
  977. DroppedDataSize );
  978. wsprintfW(count+0, L"%u", DroppedDataCount);
  979. wsprintfW(size+0, L"%u", DroppedDataSize);
  980. ClusterLogEvent3(LOG_UNUSUAL,
  981. LOG_CURRENT_MODULE,
  982. __FILE__,
  983. __LINE__,
  984. EVTLOG_DATA_DROPPED,
  985. 0,
  986. NULL,
  987. QueueName,
  988. count,
  989. size);
  990. }
  991. ////////////////////////////////////////////////////////////////////////////
  992. LARGE_INTEGER RegistryCheckInterval;
  993. LARGE_INTEGER NextRegistryCheckAt;
  994. DWORD
  995. InitializeQueues(
  996. VOID)
  997. {
  998. DWORD status, OutgoingQueueStatus;
  999. status =
  1000. SimpleQueueInitialize(
  1001. &OutgoingQueue,
  1002. OUTGOING_QUEUE_SIZE,
  1003. OUTGOING_QUEUE_NAME,
  1004. OutgoingQueueDataAvailable,
  1005. DroppedDataNotify,
  1006. DROPPED_DATA_NOTIFY_INTERVAL // seconds //
  1007. );
  1008. if (status != ERROR_SUCCESS) {
  1009. ClRtlLogPrint(LOG_CRITICAL,
  1010. "[EVT] Failed to create '%1!ws!', error %2!u!.\n",
  1011. OUTGOING_QUEUE_NAME, status );
  1012. }
  1013. OutgoingQueueStatus = status;
  1014. status =
  1015. SimpleQueueInitialize(
  1016. &IncomingQueue,
  1017. INCOMING_QUEUE_SIZE,
  1018. INCOMING_QUEUE_NAME,
  1019. IncomingQueueDataAvailable,
  1020. DroppedDataNotify,
  1021. DROPPED_DATA_NOTIFY_INTERVAL // seconds //
  1022. );
  1023. if (status != ERROR_SUCCESS) {
  1024. ClRtlLogPrint(LOG_CRITICAL,
  1025. "[EVT] Failed to create '%1!ws!', error %2!u!.\n",
  1026. INCOMING_QUEUE_NAME, status );
  1027. }
  1028. ClRtlInitializeWorkItem(
  1029. &EvtBroadcasterWorkItem,
  1030. EvtBroadcaster,
  1031. (PVOID) &OutgoingQueue
  1032. );
  1033. ClRtlInitializeWorkItem(
  1034. &EvtlogWriterWorkItem,
  1035. EvtlogWriter,
  1036. (PVOID) &IncomingQueue
  1037. );
  1038. RegistryCheckInterval.QuadPart = Int32x32To64(10 * 1000 * 1000, CHECK_CLUSTER_REGISTRY_EVERY);
  1039. NextRegistryCheckAt.QuadPart = 0;
  1040. ReadRegistryKeys();
  1041. return OutgoingQueueStatus;
  1042. }
  1043. ////////////////////////////////////////////////////////////////////////////
  1044. VOID
  1045. DestroyQueues(
  1046. VOID)
  1047. {
  1048. CheckForDroppedData(&IncomingQueue, TRUE);
  1049. CheckForDroppedData(&OutgoingQueue, TRUE);
  1050. // [GN] TODO
  1051. // Add proper destruction of queues
  1052. }
  1053. VOID
  1054. ReadRegistryKeys(
  1055. VOID)
  1056. /*
  1057. *
  1058. */
  1059. {
  1060. HDMKEY nodeKey;
  1061. DWORD NodePropagate;
  1062. DWORD ClusterPropagate;
  1063. static DWORD OldPropagateState = 0xCAFEBABE;
  1064. DWORD status;
  1065. nodeKey = DmOpenKey(
  1066. DmNodesKey,
  1067. NmLocalNodeIdString,
  1068. KEY_READ
  1069. );
  1070. if (nodeKey != NULL) {
  1071. status = DmQueryDword(
  1072. nodeKey,
  1073. CLUSREG_NAME_CLUS_EVTLOG_PROPAGATION,
  1074. &NodePropagate,
  1075. &DefaultNodePropagate
  1076. );
  1077. if (status != ERROR_SUCCESS) {
  1078. ClRtlLogPrint(
  1079. LOG_UNUSUAL,
  1080. "[EVT] Unable to query propagation mode for local node, status %1!u!.\n",
  1081. status
  1082. );
  1083. }
  1084. DmCloseKey(nodeKey);
  1085. }
  1086. else {
  1087. ClRtlLogPrint(
  1088. LOG_UNUSUAL,
  1089. "[EVT] Unable to open database key to local node, status %1!u!. Assuming default settings.\n",
  1090. GetLastError());
  1091. NodePropagate = DefaultNodePropagate;
  1092. }
  1093. status = DmQueryDword(
  1094. DmClusterParametersKey,
  1095. CLUSREG_NAME_CLUS_EVTLOG_PROPAGATION,
  1096. &ClusterPropagate,
  1097. &DefaultClusterPropagate
  1098. );
  1099. if (status != ERROR_SUCCESS) {
  1100. ClRtlLogPrint(
  1101. LOG_UNUSUAL,
  1102. "[EVT] Unable to query global propagation mode, status %1!u!.\n",
  1103. status
  1104. );
  1105. }
  1106. NodePropagate &= ClusterPropagate;
  1107. if (NodePropagate != OldPropagateState) {
  1108. ClRtlLogPrint(
  1109. LOG_UNUSUAL,
  1110. "[EVT] Set propagation state to %1!04x!\n", NodePropagate
  1111. );
  1112. if (NodePropagate & OUTGOING_PROPAGATION_ENABLED) {
  1113. if (OutgoingQueue.Begin) {
  1114. OutgoingQueue.Enabled = 1;
  1115. }
  1116. } else {
  1117. OutgoingQueue.Enabled = 0;
  1118. }
  1119. #if 0
  1120. if (NodePropagate & INCOMING_PROPAGATION_ENABLED) {
  1121. if (IncomingQueue.Begin) {
  1122. IncomingQueue.Enabled = 1;
  1123. }
  1124. } else {
  1125. IncomingQueue.Enabled = 0;
  1126. }
  1127. #endif
  1128. if(NodePropagate & TRACE_EVERYTHING_ENABLED) {
  1129. EventlogTraceEverything = 1;
  1130. } else {
  1131. EventlogTraceEverything = 0;
  1132. }
  1133. OldPropagateState = NodePropagate;
  1134. }
  1135. #ifdef EVTLOG_DELTA_GENERATION
  1136. {
  1137. DWORD dwDefaultGenerateDeltas;
  1138. dwDefaultGenerateDeltas = TRUE;
  1139. status = DmQueryDword(
  1140. DmClusterParametersKey,
  1141. CLUSREG_NAME_CLUS_EVTLOGDELTA_GENERATION,
  1142. &g_dwGenerateDeltas,
  1143. &dwDefaultGenerateDeltas
  1144. );
  1145. if (status != ERROR_SUCCESS) {
  1146. ClRtlLogPrint(
  1147. LOG_UNUSUAL,
  1148. "[EVT] Unable to query global propagation mode, status %1!u!.\n",
  1149. status
  1150. );
  1151. }
  1152. //if delta generation is true, also check the mixed mode status
  1153. //if this is less than a pure whistler cluster, turn off the delta
  1154. //generation since it doesnt make any sense unless all nodes can
  1155. //generate the time deltas.
  1156. if (g_dwGenerateDeltas)
  1157. {
  1158. if (!g_dwVersionsAllowDeltaGeneration)
  1159. g_dwGenerateDeltas = FALSE;
  1160. }
  1161. }
  1162. #endif
  1163. }
  1164. VOID
  1165. PeriodicRegistryCheck(
  1166. VOID)
  1167. {
  1168. LARGE_INTEGER currentTime;
  1169. GetSystemTimeAsFileTime( (LPFILETIME)&currentTime);
  1170. if( currentTime.QuadPart > NextRegistryCheckAt.QuadPart ) {
  1171. ReadRegistryKeys();
  1172. NextRegistryCheckAt.QuadPart = currentTime.QuadPart + RegistryCheckInterval.QuadPart;
  1173. }
  1174. }