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.

1574 lines
43 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. dminit.c
  5. Abstract:
  6. Contains the initialization code for the Cluster Database Manager
  7. Author:
  8. John Vert (jvert) 24-Apr-1996
  9. Revision History:
  10. --*/
  11. #include "dmp.h"
  12. //
  13. // Global Data
  14. //
  15. HKEY DmpRoot;
  16. HKEY DmpRootCopy;
  17. LIST_ENTRY KeyList;
  18. CRITICAL_SECTION KeyLock;
  19. HDMKEY DmClusterParametersKey;
  20. HDMKEY DmResourcesKey;
  21. HDMKEY DmResourceTypesKey;
  22. HDMKEY DmGroupsKey;
  23. HDMKEY DmNodesKey;
  24. HDMKEY DmNetworksKey;
  25. HDMKEY DmNetInterfacesKey;
  26. HDMKEY DmQuorumKey;
  27. HANDLE ghQuoLogOpenEvent=NULL;
  28. #if NO_SHARED_LOCKS
  29. CRITICAL_SECTION gLockDmpRoot;
  30. #else
  31. RTL_RESOURCE gLockDmpRoot;
  32. #endif
  33. BOOL gbIsQuoLoggingOn=FALSE;
  34. HANDLE ghDiskManTimer=NULL;//disk management timer
  35. PFM_RESOURCE gpQuoResource=NULL; //set when DMFormNewCluster is completed
  36. HANDLE ghCheckpointTimer = NULL; //timer for periodic checkpointing
  37. BOOL gbDmInited = FALSE; //set to TRUE when all phases of dm initialization are over
  38. extern HLOG ghQuoLog;
  39. BOOL gbDmpShutdownUpdates = FALSE;
  40. //define public cluster key value names
  41. const WCHAR cszPath[]= CLUSREG_NAME_QUORUM_PATH;
  42. const WCHAR cszMaxQuorumLogSize[]=CLUSREG_NAME_QUORUM_MAX_LOG_SIZE;
  43. const WCHAR cszParameters[] = CLUSREG_KEYNAME_PARAMETERS;
  44. //other const strings
  45. const WCHAR cszQuoFileName[]=L"quolog.log";
  46. const WCHAR cszQuoTombStoneFile[]=L"quotomb.stn";
  47. const WCHAR cszTmpQuoTombStoneFile[]=L"quotomb.tmp";
  48. GUM_DISPATCH_ENTRY DmGumDispatchTable[] = {
  49. {3, (PGUM_DISPATCH_ROUTINE1)DmpUpdateCreateKey},
  50. {4, (PGUM_DISPATCH_ROUTINE1)DmpUpdateSetSecurity}
  51. };
  52. //
  53. // Global data for interfacing with registry watcher thread
  54. //
  55. HANDLE hDmpRegistryFlusher=NULL;
  56. HANDLE hDmpRegistryEvent=NULL;
  57. HANDLE hDmpRegistryRestart=NULL;
  58. DWORD
  59. DmpRegistryFlusher(
  60. IN LPVOID lpThreadParameter
  61. );
  62. //
  63. // Local function prototypes
  64. //
  65. VOID
  66. DmpInvalidateKeys(
  67. VOID
  68. );
  69. VOID
  70. DmpReopenKeys(
  71. VOID
  72. );
  73. DWORD
  74. DmpLoadHive(
  75. IN LPCWSTR Path
  76. );
  77. typedef struct _DMP_KEY_DEF {
  78. HDMKEY *pKey;
  79. LPWSTR Name;
  80. } DMP_KEY_DEF;
  81. DMP_KEY_DEF DmpKeyTable[] = {
  82. {&DmResourcesKey, CLUSREG_KEYNAME_RESOURCES},
  83. {&DmResourceTypesKey, CLUSREG_KEYNAME_RESOURCE_TYPES},
  84. {&DmQuorumKey, CLUSREG_KEYNAME_QUORUM},
  85. {&DmGroupsKey, CLUSREG_KEYNAME_GROUPS},
  86. {&DmNodesKey, CLUSREG_KEYNAME_NODES},
  87. {&DmNetworksKey, CLUSREG_KEYNAME_NETWORKS},
  88. {&DmNetInterfacesKey, CLUSREG_KEYNAME_NETINTERFACES}
  89. };
  90. DWORD
  91. DmInitialize(
  92. VOID
  93. )
  94. /*++
  95. Routine Description:
  96. Inits the config database manager
  97. Arguments:
  98. None
  99. Return Value:
  100. ERROR_SUCCESS if successful
  101. Win32 error code otherwise
  102. --*/
  103. {
  104. BOOL Success;
  105. DWORD Status = ERROR_SUCCESS;
  106. DWORD dwOut;
  107. ClRtlLogPrint(LOG_NOISE,"[DM] Initialization\n");
  108. InitializeListHead(&KeyList);
  109. InitializeCriticalSection(&KeyLock);
  110. //create a critical section for locking the database while checkpointing
  111. INITIALIZE_LOCK(gLockDmpRoot);
  112. //create a named event that is used for waiting for quorum resource
  113. //to go online
  114. ghQuoLogOpenEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  115. if (!ghQuoLogOpenEvent)
  116. {
  117. CL_UNEXPECTED_ERROR((Status = GetLastError()));
  118. goto FnExit;
  119. }
  120. Success = DmpInitNotify();
  121. CL_ASSERT(Success);
  122. if (!Success)
  123. {
  124. Status = GetLastError();
  125. goto FnExit;
  126. }
  127. //find out if the databasecopy was in progresss on last death
  128. DmpGetDwordFromClusterServer(L"ClusterDatabaseCopyInProgress", &dwOut, 0);
  129. LoadClusterDatabase:
  130. //
  131. // Open key to root of cluster.
  132. //
  133. Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
  134. DmpClusterParametersKeyName,
  135. &DmpRoot);
  136. //
  137. // If the key was not found, go load the database.
  138. //
  139. if (Status == ERROR_FILE_NOT_FOUND) {
  140. WCHAR Path[MAX_PATH];
  141. WCHAR BkpPath[MAX_PATH];
  142. WCHAR *p;
  143. Status = GetModuleFileName(NULL, Path, MAX_PATH);
  144. //
  145. // GetModuleFileName may not NULL terminate the Path.
  146. //
  147. Path [ RTL_NUMBER_OF ( Path ) - 1 ] = UNICODE_NULL;
  148. if (Status == 0) {
  149. Status = GetLastError();
  150. ClRtlLogPrint(LOG_CRITICAL,
  151. "[DM] Couldn't find cluster database, status=%1!u!\n",
  152. Status);
  153. goto FnExit;
  154. }
  155. //get the name of the cluster database
  156. p=wcsrchr(Path, L'\\');
  157. if (p == NULL)
  158. {
  159. Status = ERROR_FILE_NOT_FOUND;
  160. CL_UNEXPECTED_ERROR(Status);
  161. goto FnExit;
  162. }
  163. //see if we should load the hive from the old one or the bkp file
  164. *p = L'\0';
  165. wcscpy(BkpPath, Path);
  166. #ifdef OLD_WAY
  167. wcscat(Path, L"\\CLUSDB");
  168. wcscat(BkpPath, L"\\CLUSTER_DATABASE_TMPBKP_NAME");
  169. #else // OLD_WAY
  170. wcscat(Path, L"\\"CLUSTER_DATABASE_NAME );
  171. wcscat(BkpPath, L"\\"CLUSTER_DATABASE_TMPBKP_NAME);
  172. #endif // OLD_WAY
  173. if (dwOut)
  174. {
  175. //the backip file must exist
  176. ClRtlLogPrint(LOG_NOISE,
  177. "[DM] DmInitialize:: DatabaseCopy was in progress on last death, get hive from %1!ws!!\n",
  178. BkpPath);
  179. //set file attributes of the BkpPath
  180. if (!QfsSetFileAttributes(BkpPath, FILE_ATTRIBUTE_NORMAL))
  181. {
  182. Status = GetLastError();
  183. ClRtlLogPrint(LOG_UNUSUAL,
  184. "[DM] DmInitialize:: SetFileAttrib on BkpPath %1!ws! failed, Status=%2!u!\n",
  185. BkpPath, Status);
  186. goto FnExit;
  187. }
  188. //ClRtlCopyFileAndFlushBuffers preserves the attributes on the original file
  189. if (!QfsClRtlCopyFileAndFlushBuffers(BkpPath, Path))
  190. {
  191. Status = GetLastError();
  192. ClRtlLogPrint(LOG_CRITICAL,
  193. "[DM] DmInitialize:: Databasecopy was in progress,Failed to copy %1!ws! to %2!ws!, Status=%3!u!\n",
  194. BkpPath, Path, Status);
  195. //set the file attribute on the backup, so that
  196. //nobody mucks with it without knowing what they are
  197. //doing
  198. QfsSetFileAttributes(BkpPath, FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY);
  199. goto FnExit;
  200. }
  201. //now we can reset the DatabaseCopyInProgress value in the registry
  202. //set databaseCopyInProgress key to FALSE
  203. //This will flush the key as well
  204. Status = DmpSetDwordInClusterServer( L"ClusterDatabaseCopyInProgress", 0);
  205. if (Status != ERROR_SUCCESS)
  206. {
  207. ClRtlLogPrint(LOG_CRITICAL,
  208. "[DM] DmInitialize:: Failed to reset ClusterDatabaseCopyInProgress, Status=%1!u!\n",
  209. Status);
  210. goto FnExit;
  211. }
  212. //Now we can delete the backup path, since the key has been flushed
  213. if (!QfsDeleteFile(BkpPath))
  214. {
  215. ClRtlLogPrint(LOG_CRITICAL,
  216. "[DM] DmInitialize:: Failed to delete the backup when it wasnt needed,Status=%1!u!\n",
  217. GetLastError());
  218. //this is not fatal so we ignore the error
  219. }
  220. }
  221. else
  222. {
  223. //the backup file might exist
  224. //this is true when safe copy makes a backup but hasnt
  225. //set the value DatabaseCopyInProgress in the registry
  226. //if it does delete it
  227. //set file attributes of the BkpPath
  228. if (!QfsSetFileAttributes(BkpPath, FILE_ATTRIBUTE_NORMAL))
  229. {
  230. //errors are not fatal, we just ignore them
  231. //this may fail because the path doesnt exist
  232. }
  233. //Now we can delete the backup path, since the key has been flushed
  234. //this is not fatal so we ignore the error
  235. if (QfsDeleteFile(BkpPath))
  236. {
  237. ClRtlLogPrint(LOG_NOISE,
  238. "[DM] DmInitialize:: Deleted the unneeded backup of the cluster database\n");
  239. }
  240. }
  241. Status = DmpLoadHive(Path);
  242. if (Status != ERROR_SUCCESS)
  243. {
  244. ClRtlLogPrint(LOG_CRITICAL,
  245. "[DM] Couldn't load cluster database\n");
  246. CsLogEventData(LOG_CRITICAL,
  247. DM_DATABASE_CORRUPT_OR_MISSING,
  248. sizeof(Status),
  249. &Status);
  250. goto FnExit;
  251. }
  252. Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
  253. DmpClusterParametersKeyName,
  254. &DmpRoot);
  255. //
  256. // HACKHACK John Vert (jvert) 6/3/1997
  257. // There is a bug in the registry with refresh
  258. // where the Parent field in the root cell doesn't
  259. // get flushed to disk, so it gets blasted if we
  260. // do a refresh. Then we crash in unload. So flush
  261. // out the registry to disk here to make sure the
  262. // right Parent field gets written to disk.
  263. //
  264. if (Status == ERROR_SUCCESS) {
  265. DWORD Dummy=0;
  266. //
  267. // Make something dirty in the root
  268. //
  269. RegSetValueEx(DmpRoot,
  270. L"Valid",
  271. 0,
  272. REG_DWORD,
  273. (PBYTE)&Dummy,
  274. sizeof(Dummy));
  275. RegDeleteValue(DmpRoot, L"Valid");
  276. Status = RegFlushKey(DmpRoot);
  277. }
  278. } else {
  279. //if the hive is already loaded we unload and reload it again
  280. //to make sure that it is loaded with the right flags and
  281. //also to make sure that the backup copy is used in case
  282. //of failures
  283. ClRtlLogPrint(LOG_CRITICAL,
  284. "[DM] DmInitialize: The hive was loaded- rollback, unload and reload again\n");
  285. //BUGBUG:: currently the unload flushes the hive, ideally we
  286. //would like to unload it without flushing it
  287. //This way a part transaction wont be a part of the hive
  288. //However, if somebody messes with the cluster hive using
  289. //regedt32 and if reg_no_lazy flush is not specified, some
  290. //changes might get flushed to the hive.
  291. //We can try and do the rollback in any case,
  292. //the rollback will fail if the registry wasnt loaded with the
  293. //reg_no_lazy_flush flag.
  294. //unload it and then proceed to reload it
  295. //this will take care of situations where a half baked clusdb
  296. //gets loaded because of failures
  297. Status = DmRollbackRegistry();
  298. if (Status != ERROR_SUCCESS)
  299. {
  300. //we ignore the error
  301. Status = ERROR_SUCCESS;
  302. }
  303. RegCloseKey(DmpRoot);
  304. if ( DmpRootCopy != NULL ) {
  305. RegCloseKey(DmpRootCopy);
  306. }
  307. DmpRoot = DmpRootCopy = NULL;
  308. Status = DmpUnloadHive();
  309. if (Status != ERROR_SUCCESS)
  310. {
  311. ClRtlLogPrint(LOG_CRITICAL,
  312. "[DM] DmInitialize: DmpUnloadHive failed, Status=%1!u!\n",
  313. Status);
  314. goto FnExit;
  315. }
  316. goto LoadClusterDatabase;
  317. }
  318. if (Status != ERROR_SUCCESS) {
  319. CL_UNEXPECTED_ERROR(Status);
  320. goto FnExit;
  321. }
  322. Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
  323. DmpClusterParametersKeyName,
  324. &DmpRootCopy);
  325. if ( Status != ERROR_SUCCESS ) {
  326. ClRtlLogPrint(LOG_CRITICAL,
  327. "[DM] DmInitialize:: Failed to open copy of registry key,Status=%1!u!\n",
  328. Status);
  329. goto FnExit;
  330. }
  331. //
  332. // Create the registry watcher thread
  333. //
  334. Status = DmpStartFlusher();
  335. if (Status != ERROR_SUCCESS) {
  336. goto FnExit;
  337. }
  338. //
  339. // Open the cluster keys
  340. //
  341. Status = DmpOpenKeys(MAXIMUM_ALLOWED);
  342. if (Status != ERROR_SUCCESS) {
  343. CL_UNEXPECTED_ERROR( Status );
  344. goto FnExit;
  345. }
  346. FnExit:
  347. return(Status);
  348. }//DmInitialize
  349. DWORD
  350. DmpRegistryFlusher(
  351. IN LPVOID lpThreadParameter
  352. )
  353. /*++
  354. Routine Description:
  355. Registry watcher thread for explicitly flushing changes.
  356. Arguments:
  357. lpThreadParameter - not used
  358. Return Value:
  359. None.
  360. --*/
  361. {
  362. DWORD Status;
  363. HANDLE hEventFullSubtree = NULL;
  364. HANDLE hTimer = NULL;
  365. HANDLE hEventTopOnly = NULL;
  366. HANDLE WaitArray[5];
  367. LARGE_INTEGER DueTime;
  368. BOOL Dirty = FALSE;
  369. BOOL subtreeNeedsReg = TRUE;
  370. BOOL topNeedsReg = TRUE;
  371. //
  372. // Create a notification event and a delayed timer for lazy flushing.
  373. //
  374. hEventFullSubtree = CreateEvent(NULL, TRUE, FALSE, NULL);
  375. if (hEventFullSubtree == NULL) {
  376. Status = GetLastError();
  377. ClRtlLogPrint(LOG_CRITICAL,
  378. "[DM] DmpRegistryFlusher couldn't create notification event %1!d!\n",
  379. Status);
  380. goto error_exit;
  381. }
  382. hTimer = CreateWaitableTimer(NULL, FALSE, NULL);
  383. if (hTimer == NULL) {
  384. Status = GetLastError();
  385. ClRtlLogPrint(LOG_CRITICAL,
  386. "[DM] DmpRegistryFlusher couldn't create notification timer %1!d!\n",
  387. Status);
  388. goto error_exit;
  389. }
  390. hEventTopOnly = CreateEvent(NULL, TRUE, FALSE, NULL);
  391. if (hEventTopOnly == NULL) {
  392. Status = GetLastError();
  393. ClRtlLogPrint(LOG_CRITICAL,
  394. "[DM] DmpRegistryFlusher couldn't create notification event %1!d!\n",
  395. Status);
  396. goto error_exit;
  397. }
  398. WaitArray[0] = hDmpRegistryEvent;
  399. WaitArray[1] = hEventFullSubtree;
  400. WaitArray[2] = hTimer;
  401. WaitArray[3] = hDmpRegistryRestart;
  402. WaitArray[4] = hEventTopOnly;
  403. while (TRUE) {
  404. //
  405. // Set up a registry notification on DmpRoot. We acquire the lock here to
  406. // make sure that rollback or install is not messing with the database
  407. // while we are trying to get a notification.
  408. //
  409. Status = ERROR_SUCCESS;
  410. ACQUIRE_EXCLUSIVE_LOCK(gLockDmpRoot);
  411. // Because we are now waiting on 2 separate RegNotify's, we can't just
  412. // reregister for notications on every iteration. Need to see which
  413. // registration is "stale".
  414. if ( subtreeNeedsReg ) {
  415. Status = RegNotifyChangeKeyValue(DmpRoot,
  416. TRUE,
  417. REG_LEGAL_CHANGE_FILTER,
  418. hEventFullSubtree,
  419. TRUE);
  420. subtreeNeedsReg = FALSE;
  421. }
  422. if ( topNeedsReg && Status == ERROR_SUCCESS ) {
  423. Status = RegNotifyChangeKeyValue(DmpRootCopy,
  424. FALSE, // not whole subtree
  425. REG_LEGAL_CHANGE_FILTER,
  426. hEventTopOnly,
  427. TRUE);
  428. topNeedsReg = FALSE;
  429. }
  430. RELEASE_LOCK(gLockDmpRoot);
  431. if (Status != ERROR_SUCCESS) {
  432. ClRtlLogPrint(LOG_CRITICAL,
  433. "[DM] DmpRegistryFlusher couldn't register for notifications %1!d!\n",
  434. Status);
  435. break;
  436. }
  437. //
  438. // Wait for something to happen.
  439. //
  440. Status = WaitForMultipleObjects(sizeof(WaitArray)/sizeof(WaitArray[0]),
  441. WaitArray,
  442. FALSE,
  443. (DWORD)-1);
  444. switch (Status) {
  445. case 0:
  446. ClRtlLogPrint(LOG_NOISE,"[DM] DmpRegistryFlusher: got 0\r\n");
  447. //
  448. // We have been asked to stop, clean up and exit
  449. //
  450. Status = ERROR_SUCCESS;
  451. if (Dirty) {
  452. //
  453. // Make sure any changes that we haven't gotten around to flushing
  454. // get flushed now.
  455. //
  456. DmCommitRegistry();
  457. }
  458. ClRtlLogPrint(LOG_NOISE,"[DM] DmpRegistryFlusher: exiting\r\n");
  459. goto error_exit;
  460. break;
  461. case 1:
  462. // The subtree RegNotify is now stale and needs to be reregistered.
  463. subtreeNeedsReg = TRUE;
  464. //
  465. // A registry change has occurred. Set our timer to
  466. // go off in 5 seconds. At that point we will do the
  467. // actual flush.
  468. //
  469. //ClRtlLogPrint(LOG_NOISE,"[DM] DmpRegistryFlusher: got 1\r\n");
  470. DueTime.QuadPart = -5 * 10 * 1000 * 1000;
  471. if (!SetWaitableTimer(hTimer,
  472. &DueTime,
  473. 0,
  474. NULL,
  475. NULL,
  476. FALSE)) {
  477. //
  478. // Some error occurred, go ahead and flush now.
  479. //
  480. Status = GetLastError();
  481. ClRtlLogPrint(LOG_CRITICAL,
  482. "[DM] DmpRegistryFlusher failed to set lazy flush timer %1!d!\n",
  483. Status);
  484. #if DBG
  485. CL_ASSERT(FALSE);
  486. #endif
  487. DmCommitRegistry();
  488. Dirty = FALSE;
  489. } else {
  490. Dirty = TRUE;
  491. }
  492. break;
  493. case 2:
  494. //
  495. // The lazy flush timer has gone off, commit the registry now.
  496. //
  497. //ClRtlLogPrint(LOG_NOISE,"[DM] DmpRegistryFlusher: got 2\r\n");
  498. DmCommitRegistry();
  499. Dirty = FALSE;
  500. break;
  501. case 3:
  502. //
  503. // DmpRoot has been changed, restart the loop with the new handle.
  504. //
  505. ClRtlLogPrint(LOG_NOISE,"[DM] DmpRegistryFlusher: restarting\n");
  506. // Because the HKEYs have been closed, both RegNotify's are
  507. // now stale and need to be reregistered.
  508. subtreeNeedsReg = topNeedsReg = TRUE;
  509. break;
  510. case 4:
  511. //
  512. // Since this registry change may have come from a DM update, the
  513. // parameters under the Cluster key may have changed on another node.
  514. // Update our in-memory variables to reflect the current registry settings.
  515. //
  516. CsRefreshGlobalsFromRegistry();
  517. // The top RegNotify is now stale and needs to be reregistered.
  518. topNeedsReg = TRUE;
  519. break;
  520. default:
  521. //
  522. // Something very odd has happened
  523. //
  524. ClRtlLogPrint(LOG_CRITICAL,
  525. "[DM] DmpRegistryFlusher got error %1!d! from WaitForMultipleObjects\n",
  526. Status);
  527. goto error_exit;
  528. } // switch
  529. } // while TRUE
  530. error_exit:
  531. if ( hEventTopOnly != NULL )
  532. CloseHandle(hEventTopOnly);
  533. if ( hTimer != NULL )
  534. CloseHandle(hTimer);
  535. if ( hEventFullSubtree != NULL )
  536. CloseHandle(hEventFullSubtree);
  537. if (Status != ERROR_SUCCESS) {
  538. ClRtlLogPrint(LOG_CRITICAL,
  539. "[DM] DmpRegistryFlusher exiting abnormally, status %1!d!\n",
  540. Status);
  541. }
  542. return(Status);
  543. }
  544. DWORD
  545. DmJoin(
  546. IN RPC_BINDING_HANDLE RpcBinding,
  547. OUT DWORD *StartSeq
  548. )
  549. /*++
  550. Routine Description:
  551. Performs the join and synchronization process for the
  552. database manager.
  553. Arguments:
  554. RpcBinding - Supplies an RPC binding handle to the Join Master
  555. Return Value:
  556. ERROR_SUCCESS if successful
  557. Win32 error otherwise.
  558. --*/
  559. {
  560. DWORD Status;
  561. DWORD GumSequence;
  562. DWORD CurrentSequence;
  563. //
  564. // Register our update handler.
  565. //
  566. GumReceiveUpdates(TRUE,
  567. GumUpdateRegistry,
  568. DmpUpdateHandler,
  569. DmWriteToQuorumLog,
  570. sizeof(DmGumDispatchTable)/sizeof(GUM_DISPATCH_ENTRY),
  571. DmGumDispatchTable,
  572. NULL);
  573. retry:
  574. CurrentSequence = DmpGetRegistrySequence();
  575. Status = GumBeginJoinUpdate(GumUpdateRegistry, &GumSequence);
  576. if (Status != ERROR_SUCCESS) {
  577. ClRtlLogPrint(LOG_CRITICAL,
  578. "[DM] GumBeginJoinUpdate failed %1!d!\n",
  579. Status);
  580. return(Status);
  581. }
  582. /*
  583. if (CurrentSequence == GumSequence) {
  584. //
  585. // Our registry sequence already matches. No need to slurp
  586. // down a new copy.
  587. //
  588. ClRtlLogPrint(LOG_NOISE,
  589. "[DM] DmJoin: registry database is up-to-date\n");
  590. } else
  591. */
  592. //SS: always get the database irrespective of the sequence numbers
  593. //this is because transactions may be lost in the log file due
  594. //to the fact that it is not write through and because of certain
  595. //race conditions in down notifications vs gum failure conditions.
  596. {
  597. ClRtlLogPrint(LOG_NOISE,
  598. "[DM] DmJoin: getting new registry database\n");
  599. Status = DmpSyncDatabase(RpcBinding, NULL);
  600. if (Status != ERROR_SUCCESS) {
  601. ClRtlLogPrint(LOG_UNUSUAL,
  602. "[DM] DmJoin: DmpSyncDatabase failed %1!d!\n",
  603. Status);
  604. return(Status);
  605. }
  606. }
  607. //
  608. // Issue GUM join update
  609. //
  610. Status = GumEndJoinUpdate(GumSequence,
  611. GumUpdateRegistry,
  612. DmUpdateJoin,
  613. 0,
  614. NULL);
  615. if (Status == ERROR_CLUSTER_DATABASE_SEQMISMATCH) {
  616. ClRtlLogPrint(LOG_UNUSUAL,
  617. "[DM] GumEndJoinUpdate with sequence %1!d! failed with a sequence mismatch\n",
  618. GumSequence);
  619. goto retry;
  620. } else if (Status != ERROR_SUCCESS) {
  621. ClRtlLogPrint(LOG_CRITICAL,
  622. "[DM] GumEndJoinUpdate with sequence %1!d! failed with status %2!d!\n",
  623. GumSequence,
  624. Status);
  625. return(Status);
  626. }
  627. *StartSeq = GumSequence;
  628. return(ERROR_SUCCESS);
  629. } // DmJoin
  630. /*
  631. DWORD
  632. DmFormNewCluster(
  633. VOID
  634. )
  635. {
  636. DWORD Status;
  637. //
  638. // Set the current GUM sequence to be one more than the one in the registry.
  639. //
  640. // SS: this will be the one to be used for the next gum transaction,
  641. // it should be one than the current as the logger discards the first of
  642. // every record the same transaction number to resolve changes made when the
  643. // locker/logger node dies in the middle of a transaction
  644. GumSetCurrentSequence(GumUpdateRegistry, (DmpGetRegistrySequence()+1));
  645. return(ERROR_SUCCESS);
  646. } // DmFormNewCluster
  647. */
  648. DWORD
  649. DmFormNewCluster(
  650. VOID
  651. )
  652. /*++
  653. Routine Description:
  654. This routine sets the gum sequence number from the registry before
  655. logs are unrolled and prepares the quorum object for quorum logging.
  656. It also hooks events for node up/down notifications.
  657. Arguments:
  658. None.
  659. Return Value:
  660. ERROR_SUCCESS if successful.
  661. A Win32 error code on failure.
  662. --*/
  663. {
  664. DWORD dwError=ERROR_SUCCESS;
  665. //
  666. // Set the current GUM sequence to be one more than the one in the registry.
  667. //
  668. // SS: this will be the one to be used for the next gum transaction,
  669. // it should be one than the current as the logger discards the first of
  670. // every record the same transaction number to resolve changes made when the
  671. // locker/logger node dies in the middle of a transaction
  672. GumSetCurrentSequence(GumUpdateRegistry, (DmpGetRegistrySequence()+1));
  673. //
  674. // Register our update handler.
  675. //
  676. GumReceiveUpdates(FALSE,
  677. GumUpdateRegistry,
  678. DmpUpdateHandler,
  679. DmWriteToQuorumLog,
  680. sizeof(DmGumDispatchTable)/sizeof(GUM_DISPATCH_ENTRY),
  681. DmGumDispatchTable,
  682. NULL);
  683. //hook the callback for node related notification with the event processor
  684. if (dwError = DmpHookEventHandler())
  685. {
  686. ClRtlLogPrint(LOG_UNUSUAL,
  687. "[DM] DmUpdateFormNewCluster: DmpHookEventHandler failed 0x!08lx!\r\n",
  688. dwError);
  689. goto FnExit;
  690. };
  691. //get the quorum resource and hook the callback for notification on quorum resource
  692. if (dwError = DmpHookQuorumNotify())
  693. {
  694. ClRtlLogPrint(LOG_UNUSUAL,
  695. "[DM] DmUpdateFormNewCluster: DmpHookQuorumNotify failed 0x%1!08lx!\r\n",
  696. dwError);
  697. goto FnExit;
  698. };
  699. //SS: if this procedure is successfully completed gpQuoResource is NON NULL.
  700. FnExit:
  701. return(dwError);
  702. } // DmUpdateFormNewCluster
  703. DWORD
  704. DmUpdateFormNewCluster(
  705. VOID
  706. )
  707. /*++
  708. Routine Description:
  709. This routine updates the cluster registry after the quorum resource has
  710. been arbitrated as part of forming a new cluster. The database manager
  711. is expected to read logs or do whatever it needs to update the current
  712. state of the registry - presumably using logs that are written to the
  713. quorum resource. This implies that the quorum resource represents some
  714. form of stable storage.
  715. Arguments:
  716. None.
  717. Return Value:
  718. ERROR_SUCCESS if successful.
  719. A Win32 error code on failure.
  720. --*/
  721. {
  722. DWORD dwError=ERROR_SUCCESS;
  723. BOOL bAreAllNodesUp = TRUE; //assume all nodes are up
  724. //since we havent been logging as yet, take a checkpoint
  725. if (ghQuoLog)
  726. {
  727. //get a checkpoint database
  728. ClRtlLogPrint(LOG_NOISE,
  729. "[DM] DmUpdateFormNewCluster - taking a checkpoint\r\n");
  730. //
  731. // Chittur Subbaraman (chitturs) - 6/3/99
  732. //
  733. // Make sure the gLockDmpRoot is held before LogCheckPoint is called
  734. // so as to maintain the ordering between this lock and the log lock.
  735. //
  736. ACQUIRE_SHARED_LOCK(gLockDmpRoot);
  737. dwError = LogCheckPoint(ghQuoLog, TRUE, NULL, 0);
  738. RELEASE_LOCK(gLockDmpRoot);
  739. if (dwError != ERROR_SUCCESS)
  740. {
  741. ClRtlLogPrint(LOG_CRITICAL,
  742. "[DM] DmUpdateFormNewCluster - Failed to take a checkpoint in the log file\r\n");
  743. CL_UNEXPECTED_ERROR(dwError);
  744. }
  745. }
  746. //if all nodes are not up, turn quorum logging on
  747. if ((dwError = OmEnumObjects(ObjectTypeNode, DmpNodeObjEnumCb, &bAreAllNodesUp, NULL))
  748. != ERROR_SUCCESS)
  749. {
  750. ClRtlLogPrint(LOG_UNUSUAL,
  751. "[DM] DmUpdateFormNewCluster : OmEnumObjects returned 0x%1!08lx!\r\n",
  752. dwError);
  753. goto FnExit;
  754. }
  755. if (!bAreAllNodesUp)
  756. {
  757. ClRtlLogPrint(LOG_NOISE,
  758. "[DM] DmUpdateFormNewCluster - some node down\r\n");
  759. gbIsQuoLoggingOn = TRUE;
  760. }
  761. //add a timer to monitor disk space, should be done after we have formed.
  762. ghDiskManTimer = CreateWaitableTimer(NULL, FALSE, NULL);
  763. if (!ghDiskManTimer)
  764. {
  765. CL_LOGFAILURE(dwError = GetLastError());
  766. goto FnExit;
  767. }
  768. AddTimerActivity(ghDiskManTimer, DISKSPACE_MANAGE_INTERVAL, 1, DmpDiskManage, NULL);
  769. gbDmInited = TRUE;
  770. FnExit:
  771. return (dwError);
  772. } // DmFormNewCluster
  773. /****
  774. @func DWORD | DmPauseDiskManTimer| The disk manager timer activity to monitor
  775. space on the quorum disk is set to a puased state.
  776. @rdesc Returns ERROR_SUCCESS on success. Else returns the error code.
  777. @comm This is called while the quorum resource is being changed.
  778. @xref <f DmRestartDiskManTimer>
  779. ****/
  780. DWORD DmPauseDiskManTimer()
  781. {
  782. DWORD dwError=ERROR_SUCCESS;
  783. if (ghDiskManTimer)
  784. dwError = PauseTimerActivity(ghDiskManTimer);
  785. return(dwError);
  786. }
  787. /****
  788. @func DWORD | DmRestartDiskManTimer| This disk manager activity to monitor
  789. space on the quorum disk is set back to activated state.
  790. @rdesc Returns ERROR_SUCCESS on success. Else returns the error code.
  791. @comm This is called after the quorum resource has been changed.
  792. @xref <f DmPauseDiskManTimer>
  793. ****/
  794. DWORD DmRestartDiskManTimer()
  795. {
  796. DWORD dwError=ERROR_SUCCESS;
  797. if (ghDiskManTimer)
  798. dwError = UnpauseTimerActivity(ghDiskManTimer);
  799. return(dwError);
  800. }
  801. /****
  802. @func DWORD | DmRollChanges| This waits for the quorum resource to come online at
  803. initialization when a cluster is being formed. The changes in the quorum
  804. log file are applied to the local cluster database.
  805. @rdesc Returns ERROR_SUCCESS on success. Else returns the error code.
  806. @comm This allows for partitions in time.
  807. @xref
  808. ****/
  809. DWORD DmRollChanges()
  810. {
  811. DWORD dwError=ERROR_SUCCESS;
  812. //before applying the changes validate that this quorum resource is the real one
  813. if ((dwError = DmpChkQuoTombStone()) != ERROR_SUCCESS)
  814. {
  815. ClRtlLogPrint(LOG_UNUSUAL,
  816. "[DM] DmRollChanges: DmpChkQuoTombStone() failed 0x%1!08lx!\r\n",
  817. dwError);
  818. goto FnExit;
  819. }
  820. if ((dwError = DmpApplyChanges()) != ERROR_SUCCESS)
  821. {
  822. ClRtlLogPrint(LOG_UNUSUAL,
  823. "[DM] DmRollChanges: DmpApplyChanges() failed 0x%1!08lx!\r\n",
  824. dwError);
  825. goto FnExit;
  826. }
  827. //ss: this is here since lm doesnt know about the ownership of quorum
  828. //disks today
  829. //call DmpCheckSpace
  830. if ((dwError = DmpCheckDiskSpace()) != ERROR_SUCCESS)
  831. {
  832. ClRtlLogPrint(LOG_UNUSUAL,
  833. "[DM] DmRollChanges: DmpCheckDiskSpace() failed 0x%1!08lx!\r\n",
  834. dwError);
  835. goto FnExit;
  836. }
  837. FnExit:
  838. return(dwError);
  839. }
  840. DWORD DmShutdown()
  841. {
  842. DWORD dwError;
  843. ClRtlLogPrint(LOG_NOISE,
  844. "[Dm] DmShutdown\r\n");
  845. //this will close the timer handle
  846. if (ghDiskManTimer) RemoveTimerActivity(ghDiskManTimer);
  847. if (gpQuoResource)
  848. {
  849. // DmFormNewCluster() completed
  850. //
  851. // Deregister from any further GUM updates
  852. //
  853. //GumIgnoreUpdates(GumUpdateRegistry, DmpUpdateHandler);
  854. }
  855. //unhook the callback for notification on quorum resource
  856. if (dwError = DmpUnhookQuorumNotify())
  857. {
  858. //just log the error as we are shutting down
  859. ClRtlLogPrint(LOG_UNUSUAL,
  860. "[DM] DmShutdown: DmpUnhookQuorumNotify failed 0x%1!08lx!\r\n",
  861. dwError);
  862. }
  863. //if the quorum log is open close it
  864. if (ghQuoLog)
  865. {
  866. LogClose(ghQuoLog);
  867. ghQuoLog = NULL;
  868. //dont try and log after this
  869. gbIsQuoLoggingOn = FALSE;
  870. }
  871. //close the event created for notification of the quorum resource to
  872. //go online
  873. if (ghQuoLogOpenEvent)
  874. {
  875. //wait any thread blocked on this
  876. SetEvent(ghQuoLogOpenEvent);
  877. CloseHandle(ghQuoLogOpenEvent);
  878. ghQuoLogOpenEvent = NULL;
  879. }
  880. //
  881. // Shut down the registry flusher thread.
  882. //
  883. DmpShutdownFlusher();
  884. return(dwError);
  885. }
  886. DWORD
  887. DmpStartFlusher(
  888. VOID
  889. )
  890. /*++
  891. Routine Description:
  892. Starts up a new registry flusher thread.
  893. Arguments:
  894. None.
  895. Return Value:
  896. ERROR_SUCCESS if successful
  897. Win32 error code otherwise
  898. --*/
  899. {
  900. DWORD ThreadId;
  901. ClRtlLogPrint(LOG_NOISE,"[DM] DmpStartFlusher: Entry\r\n");
  902. if (!hDmpRegistryFlusher)
  903. {
  904. hDmpRegistryEvent = CreateEventW(NULL,FALSE,FALSE,NULL);
  905. if (hDmpRegistryEvent == NULL) {
  906. return(GetLastError());
  907. }
  908. hDmpRegistryRestart = CreateEventW(NULL,FALSE,FALSE,NULL);
  909. if (hDmpRegistryRestart == NULL) {
  910. CloseHandle(hDmpRegistryEvent);
  911. return(GetLastError());
  912. }
  913. hDmpRegistryFlusher = CreateThread(NULL,
  914. 0,
  915. DmpRegistryFlusher,
  916. NULL,
  917. 0,
  918. &ThreadId);
  919. if (hDmpRegistryFlusher == NULL) {
  920. CloseHandle(hDmpRegistryRestart);
  921. CloseHandle(hDmpRegistryEvent);
  922. return(GetLastError());
  923. }
  924. ClRtlLogPrint(LOG_NOISE,"[DM] DmpStartFlusher: thread created\r\n");
  925. }
  926. return(ERROR_SUCCESS);
  927. }
  928. VOID
  929. DmpShutdownFlusher(
  930. VOID
  931. )
  932. /*++
  933. Routine Description:
  934. Cleanly shutsdown the registry flusher thread.
  935. Arguments:
  936. None.
  937. Return Value:
  938. None.
  939. --*/
  940. {
  941. ClRtlLogPrint(LOG_NOISE,"[DM] DmpShutdownFlusher: Entry\r\n");
  942. if (hDmpRegistryFlusher) {
  943. ClRtlLogPrint(LOG_NOISE,"[DM] DmpShutdownFlusher: Setting event\r\n");
  944. SetEvent(hDmpRegistryEvent);
  945. WaitForSingleObject(hDmpRegistryFlusher, INFINITE);
  946. CloseHandle(hDmpRegistryFlusher);
  947. hDmpRegistryFlusher = NULL;
  948. CloseHandle(hDmpRegistryEvent);
  949. CloseHandle(hDmpRegistryRestart);
  950. hDmpRegistryEvent = NULL;
  951. hDmpRegistryRestart = NULL;
  952. }
  953. }
  954. VOID
  955. DmpRestartFlusher(
  956. VOID
  957. )
  958. /*++
  959. Routine Description:
  960. Restarts the registry flusher thread if DmpRoot is being changed.
  961. N.B. In order for this to work correctly, gLockDmpRoot MUST be held!
  962. Arguments:
  963. None.
  964. Return Value:
  965. None.
  966. --*/
  967. {
  968. ClRtlLogPrint(LOG_NOISE,"[DM] DmpRestartFlusher: Entry\r\n");
  969. #if NO_SHARED_LOCKS
  970. CL_ASSERT(HandleToUlong(gLockDmpRoot.OwningThread) == GetCurrentThreadId());
  971. #else
  972. CL_ASSERT(HandleToUlong(gLockDmpRoot.ExclusiveOwnerThread) == GetCurrentThreadId());
  973. #endif
  974. if (hDmpRegistryRestart) {
  975. // GorN 11/11/2001 DmpRestart flusher could be called before the hDmpRegistryRestart is set
  976. SetEvent(hDmpRegistryRestart);
  977. }
  978. }
  979. DWORD
  980. DmUpdateJoinCluster(
  981. VOID
  982. )
  983. /*++
  984. Routine Description:
  985. This routine is called after a node has successfully joined a cluster.
  986. It allows the DM to hook callbacks for node up/down notifications and for
  987. quorum resource change notification.
  988. Arguments:
  989. None.
  990. Return Value:
  991. ERROR_SUCCESS if successful.
  992. A Win32 error code on failure.
  993. --*/
  994. {
  995. DWORD dwError=ERROR_SUCCESS;
  996. BOOL bAreAllNodesUp = FALSE;
  997. ClRtlLogPrint(LOG_NOISE,
  998. "[DM] DmUpdateJoinCluster: Begin.\r\n");
  999. //if all nodes are not up, turn quorum logging on
  1000. if ((dwError = OmEnumObjects(ObjectTypeNode, DmpNodeObjEnumCb, &bAreAllNodesUp, NULL))
  1001. != ERROR_SUCCESS)
  1002. {
  1003. ClRtlLogPrint(LOG_UNUSUAL,
  1004. "[DM] DmUpdateJoinCluster : OmEnumObjects returned 0x%1!08lx!\r\n",
  1005. dwError);
  1006. goto FnExit;
  1007. }
  1008. if (!bAreAllNodesUp)
  1009. {
  1010. ClRtlLogPrint(LOG_NOISE,
  1011. "[DM] DmUpdateJoinCluster - some node down\n");
  1012. gbIsQuoLoggingOn = TRUE;
  1013. }
  1014. //hook the notification for node up/down so we can keep track of whether logging
  1015. //should be on or off.
  1016. if (dwError = DmpHookEventHandler())
  1017. {
  1018. //BUGBUG SS: do we log this or return this error code
  1019. ClRtlLogPrint(LOG_UNUSUAL,
  1020. "[DM] DmUpdateJoinCluster: DmpHookEventHandler failed 0x%1!08lx!\r\n",
  1021. dwError);
  1022. }
  1023. //hook the callback for notification on quorum resource
  1024. if (dwError = DmpHookQuorumNotify())
  1025. {
  1026. ClRtlLogPrint(LOG_UNUSUAL,
  1027. "[DM] DmUpdateJoinCluster: DmpHookQuorumNotify failed 0x%1!08lx!\r\n",
  1028. dwError);
  1029. goto FnExit;
  1030. }
  1031. if ((dwError = DmpCheckDiskSpace()) != ERROR_SUCCESS)
  1032. {
  1033. ClRtlLogPrint(LOG_UNUSUAL,
  1034. "[DM] DmUpdateJoinCluster: DmpCheckDiskSpace() failed 0x%1!08lx!\r\n",
  1035. dwError);
  1036. goto FnExit;
  1037. }
  1038. //add a timer to monitor disk space, should be done after we have joined.
  1039. ghDiskManTimer = CreateWaitableTimer(NULL, FALSE, NULL);
  1040. if (!ghDiskManTimer)
  1041. {
  1042. CL_LOGFAILURE(dwError = GetLastError());
  1043. goto FnExit;
  1044. }
  1045. //register a periodic timer
  1046. AddTimerActivity(ghDiskManTimer, DISKSPACE_MANAGE_INTERVAL, 1, DmpDiskManage, NULL);
  1047. gbDmInited = TRUE;
  1048. FnExit:
  1049. return(dwError);
  1050. } // DmUpdateJoinCluster
  1051. DWORD
  1052. DmpOpenKeys(
  1053. IN REGSAM samDesired
  1054. )
  1055. /*++
  1056. Routine Description:
  1057. Opens all the standard cluster registry keys. If any of the
  1058. keys are already opened, they will be closed and reopened.
  1059. Arguments:
  1060. samDesired - Supplies the access that the keys will be opened with.
  1061. Return Value:
  1062. ERROR_SUCCESS if successful.
  1063. Win32 error code otherwise.
  1064. --*/
  1065. {
  1066. DWORD i;
  1067. DWORD Status;
  1068. DmClusterParametersKey = DmGetRootKey( MAXIMUM_ALLOWED );
  1069. if ( DmClusterParametersKey == NULL ) {
  1070. Status = GetLastError();
  1071. CL_UNEXPECTED_ERROR(Status);
  1072. return(Status);
  1073. }
  1074. for (i=0;
  1075. i<sizeof(DmpKeyTable)/sizeof(DMP_KEY_DEF);
  1076. i++) {
  1077. *DmpKeyTable[i].pKey = DmOpenKey(DmClusterParametersKey,
  1078. DmpKeyTable[i].Name,
  1079. samDesired);
  1080. if (*DmpKeyTable[i].pKey == NULL) {
  1081. Status = GetLastError();
  1082. ClRtlLogPrint(LOG_CRITICAL,
  1083. "[DM] Failed to open key %1!ws!, status %2!u!\n",
  1084. DmpKeyTable[i].Name,
  1085. Status);
  1086. CL_UNEXPECTED_ERROR( Status );
  1087. return(Status);
  1088. }
  1089. }
  1090. return(ERROR_SUCCESS);
  1091. }
  1092. VOID
  1093. DmpInvalidateKeys(
  1094. VOID
  1095. )
  1096. /*++
  1097. Routine Description:
  1098. Invalidates all open cluster registry keys.
  1099. Arguments:
  1100. None.
  1101. Return Value:
  1102. None.
  1103. --*/
  1104. {
  1105. PLIST_ENTRY ListEntry;
  1106. PDMKEY Key;
  1107. ListEntry = KeyList.Flink;
  1108. while (ListEntry != &KeyList) {
  1109. Key = CONTAINING_RECORD(ListEntry,
  1110. DMKEY,
  1111. ListEntry);
  1112. if (!Key->hKey)
  1113. {
  1114. ClRtlLogPrint(LOG_CRITICAL,
  1115. "[DM] DmpInvalidateKeys %1!ws! Key was deleted since last reopen but not closed\n",
  1116. Key->Name);
  1117. ClRtlLogPrint(LOG_CRITICAL,
  1118. "[DM] THIS MAY BE A KEY LEAK !!\r\n");
  1119. }
  1120. else
  1121. {
  1122. RegCloseKey(Key->hKey);
  1123. Key->hKey = NULL;
  1124. }
  1125. ListEntry = ListEntry->Flink;
  1126. }
  1127. }
  1128. VOID
  1129. DmpReopenKeys(
  1130. VOID
  1131. )
  1132. /*++
  1133. Routine Description:
  1134. Reopens all the keys that were invalidated by DmpInvalidateKeys
  1135. Arguments:
  1136. None
  1137. Return Value:
  1138. None.
  1139. --*/
  1140. {
  1141. PLIST_ENTRY ListEntry;
  1142. PDMKEY Key;
  1143. DWORD Status;
  1144. ListEntry = KeyList.Flink;
  1145. while (ListEntry != &KeyList) {
  1146. Key = CONTAINING_RECORD(ListEntry,
  1147. DMKEY,
  1148. ListEntry);
  1149. CL_ASSERT(Key->hKey == NULL);
  1150. Status = RegOpenKeyEx(DmpRoot,
  1151. Key->Name,
  1152. 0,
  1153. Key->GrantedAccess,
  1154. &Key->hKey);
  1155. if (Status != ERROR_SUCCESS) {
  1156. ClRtlLogPrint(LOG_CRITICAL,"[DM] Could not reopen key %1!ws! error %2!d!\n",Key->Name,Status);
  1157. // if the error is file not found, then the key was deleted while the handle
  1158. // was open. Set the key to NULL
  1159. // If the key is used after delete, it should be validated
  1160. if (Status == ERROR_FILE_NOT_FOUND)
  1161. Key->hKey = NULL;
  1162. else
  1163. CL_UNEXPECTED_ERROR(Status);
  1164. }
  1165. ListEntry = ListEntry->Flink;
  1166. }
  1167. }
  1168. DWORD
  1169. DmpGetRegistrySequence(
  1170. VOID
  1171. )
  1172. /*++
  1173. Routine Description:
  1174. Returns the current registry sequence stored in the registry.
  1175. Arguments:
  1176. None.
  1177. Return Value:
  1178. The current registry sequence.
  1179. --*/
  1180. {
  1181. DWORD Length;
  1182. DWORD Type;
  1183. DWORD Sequence;
  1184. DWORD Status;
  1185. Length = sizeof(Sequence);
  1186. Status = RegQueryValueExW(DmpRoot,
  1187. CLUSREG_NAME_CLUS_REG_SEQUENCE,
  1188. 0,
  1189. &Type,
  1190. (LPBYTE)&Sequence,
  1191. &Length);
  1192. if (Status != ERROR_SUCCESS) {
  1193. ClRtlLogPrint(LOG_UNUSUAL, "[DM] DmpGetRegistrySequence failed %1!u!\n",Status);
  1194. Sequence = 0;
  1195. }
  1196. return(Sequence);
  1197. }
  1198. DWORD DmWaitQuorumResOnline()
  1199. /*++
  1200. Routine Description:
  1201. Waits for quorum resource to come online. Used for quorum logging.
  1202. Arguments:
  1203. None
  1204. Return Value:
  1205. returns ERROR_SUCCESS - if the online event is signaled and the quorum
  1206. notification callback is called. Else returns the wait status.
  1207. --*/
  1208. {
  1209. // Wait indefinitely for the quorum resource to go online
  1210. DWORD dwError = ERROR_INVALID_PARAMETER;
  1211. if (ghQuoLogOpenEvent)
  1212. {
  1213. dwError = WaitForSingleObject(ghQuoLogOpenEvent, INFINITE);
  1214. switch(dwError)
  1215. {
  1216. case WAIT_OBJECT_0:
  1217. //everything is fine
  1218. dwError = ERROR_SUCCESS;
  1219. break;
  1220. case WAIT_TIMEOUT:
  1221. //couldnt roll the changes
  1222. dwError = ERROR_TIMEOUT;
  1223. ClRtlLogPrint(LOG_UNUSUAL,
  1224. "[DM] DmRollChanges: Timed out waiting on dmInitEvent\r\n");
  1225. break;
  1226. case WAIT_FAILED:
  1227. CL_ASSERT(dwError != WAIT_FAILED);
  1228. dwError = GetLastError();
  1229. ClRtlLogPrint(LOG_UNUSUAL,
  1230. "[DM] DmRollChanges: wait on dmInitEventfailed failed 0x%1!08lx!\r\n",
  1231. dwError );
  1232. break;
  1233. } // switch
  1234. }
  1235. return(dwError);
  1236. }
  1237. VOID DmShutdownUpdates(
  1238. VOID
  1239. )
  1240. /*++
  1241. Routine Description:
  1242. Shutdown DM GUM updates.
  1243. Arguments:
  1244. None
  1245. Return Value:
  1246. None.
  1247. --*/
  1248. {
  1249. gbDmpShutdownUpdates = TRUE;
  1250. }