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.

2781 lines
74 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. fminit.c
  5. Abstract:
  6. Initialization for the Failover Manager component of the
  7. NT Cluster Service
  8. Author:
  9. John Vert (jvert) 7-Feb-1996
  10. Rod Gamache (rodga) 14-Mar-1996
  11. Revision History:
  12. --*/
  13. #include "..\nm\nmp.h" /* For NmpEnumNodeDefinitions */
  14. #ifdef LOG_CURRENT_MODULE
  15. #undef LOG_CURRENT_MODULE
  16. #endif
  17. #include "fmp.h"
  18. #define LOG_MODULE FMINIT
  19. // The order in which the locks should be acquired is
  20. // 1) gQuoChangeLock
  21. // 2) GroupLock
  22. // 3) gQuoLock
  23. // 4) GumLocks
  24. // 4*) gResTypeLock - this lock is acquired inside gum updates
  25. // 5) gLockDmpRoot
  26. // 6) pLog->Lock
  27. //A lock for synchronizing online/offline with respect to the quorum
  28. //resource
  29. //This lock is held in exclusive mode when bringing the quorum resource
  30. //online/offline and in shared mode when other resources are brought online
  31. //offline
  32. #if NO_SHARED_LOCKS
  33. CRITICAL_SECTION gQuoLock;
  34. #else
  35. RTL_RESOURCE gQuoLock;
  36. #endif
  37. //A lock for synchronizing changes to the resource->quorumresource field
  38. //and allowing changes to the quorum resource's group in form phase1
  39. // and phase 2 of fm.
  40. #if NO_SHARED_LOCKS
  41. CRITICAL_SECTION gQuoChangeLock;
  42. #else
  43. RTL_RESOURCE gQuoChangeLock;
  44. #endif
  45. //A lock for synchronizing changes to the resource type field entries.
  46. //shared by all resource types.
  47. #if NO_SHARED_LOCKS
  48. CRITICAL_SECTION gResTypeLock;
  49. #else
  50. RTL_RESOURCE gResTypeLock;
  51. #endif
  52. GUM_DISPATCH_ENTRY FmGumDispatchTable[] = {
  53. {2, (PGUM_DISPATCH_ROUTINE1)FmpUpdateChangeResourceName},
  54. {2, (PGUM_DISPATCH_ROUTINE1)FmpUpdateChangeGroupName},
  55. {1, FmpUpdateDeleteResource},
  56. {1, FmpUpdateDeleteGroup},
  57. {2, (PGUM_DISPATCH_ROUTINE1)FmpUpdateAddDependency},
  58. {2, (PGUM_DISPATCH_ROUTINE1)FmpUpdateRemoveDependency},
  59. {1, FmpUpdateChangeClusterName},
  60. {3, (PGUM_DISPATCH_ROUTINE1)FmpUpdateChangeQuorumResource},
  61. {2, (PGUM_DISPATCH_ROUTINE1)FmpUpdateResourceState},
  62. {3, (PGUM_DISPATCH_ROUTINE1)FmpUpdateGroupState},
  63. {4, (PGUM_DISPATCH_ROUTINE1)EpUpdateClusWidePostEvent},
  64. {2, (PGUM_DISPATCH_ROUTINE1)FmpUpdateGroupNode},
  65. {3, (PGUM_DISPATCH_ROUTINE1)FmpUpdatePossibleNodeForResType},
  66. {2, (PGUM_DISPATCH_ROUTINE1)FmpUpdateGroupIntendedOwner},
  67. {1, (PGUM_DISPATCH_ROUTINE1)FmpUpdateAssignOwnerToGroups},
  68. {1, (PGUM_DISPATCH_ROUTINE1)FmpUpdateApproveJoin},
  69. {2, (PGUM_DISPATCH_ROUTINE1)FmpUpdateCompleteGroupMove},
  70. {2, (PGUM_DISPATCH_ROUTINE1)FmpUpdateCheckAndSetGroupOwner},
  71. {2, (PGUM_DISPATCH_ROUTINE1)FmpUpdateUseRandomizedNodeListForGroups},
  72. {5, (PGUM_DISPATCH_ROUTINE1)FmpUpdateChangeQuorumResource2},
  73. };
  74. #define WINDOW_TIMEOUT (15*60*1000) // Try every 15 minutes
  75. //
  76. // Global data initialized in this module
  77. //
  78. PRESMON FmpDefaultMonitor = NULL;
  79. DWORD FmpInitialized = FALSE;
  80. DWORD FmpFMOnline = FALSE;
  81. DWORD FmpFMGroupsInited = FALSE;
  82. DWORD FmpFMFormPhaseProcessing = FALSE; //this is set to true when form new cluster phase processing starts
  83. BOOL FmpShutdown = FALSE;
  84. BOOL FmpMajorEvent = FALSE; // Signals a major event while joining
  85. DWORD FmpQuorumOnLine = FALSE;
  86. HANDLE FmpShutdownEvent;
  87. HANDLE FmpTimerThread;
  88. HANDLE ghQuoOnlineEvent = NULL; // the event that is signalled when the quorum res is online
  89. DWORD gdwQuoBlockingResources = 0; // the number of resources in pending stated which prevent the quorum res state change
  90. PFM_NODE gFmpNodeArray = NULL;
  91. // 185575: remove unique RPC binding handles
  92. //CRITICAL_SECTION FmpBindingLock;
  93. //
  94. // Local functions
  95. //
  96. BOOL
  97. FmpEnumNodes(
  98. OUT DWORD *pStatus,
  99. IN PVOID Context2,
  100. IN PNM_NODE Node,
  101. IN LPCWSTR Name
  102. );
  103. DWORD
  104. FmpJoinPendingThread(
  105. IN LPVOID Context
  106. );
  107. DWORD FmpGetJoinApproval();
  108. static
  109. DWORD
  110. FmpBuildForceQuorumInfo(
  111. IN LPCWSTR pszNodesIn,
  112. OUT PCLUS_FORCE_QUORUM_INFO* ppForceQuorumInfo
  113. );
  114. static
  115. void
  116. FmpDeleteForceQuorumInfo(
  117. IN OUT PCLUS_FORCE_QUORUM_INFO* ppForceQuorumInfo
  118. );
  119. DWORD
  120. WINAPI
  121. FmInitialize(
  122. VOID
  123. )
  124. /*++
  125. Routine Description:
  126. Initializes the failover manager
  127. Arguments:
  128. None
  129. Return Value:
  130. ERROR_SUCCESS if successful.
  131. Win32 error code otherwise.
  132. --*/
  133. {
  134. DWORD Status;
  135. OM_OBJECT_TYPE_INITIALIZE ObjectTypeInit;
  136. DWORD NodeId;
  137. CL_ASSERT(!FmpInitialized);
  138. if ( FmpInitialized ) {
  139. return(ERROR_SUCCESS);
  140. }
  141. Status = EpRegisterEventHandler(CLUSTER_EVENT_ALL,FmpEventHandler);
  142. if (Status != ERROR_SUCCESS) {
  143. CsInconsistencyHalt( Status );
  144. }
  145. //register for synchronous node down notifications
  146. Status = EpRegisterSyncEventHandler(CLUSTER_EVENT_NODE_DOWN_EX,
  147. FmpSyncEventHandler);
  148. if (Status != ERROR_SUCCESS){
  149. CsInconsistencyHalt( Status );
  150. }
  151. //
  152. // Initialize Critical Sections.
  153. //
  154. InitializeCriticalSection( &FmpResourceLock );
  155. InitializeCriticalSection( &FmpGroupLock );
  156. InitializeCriticalSection( &FmpMonitorLock );
  157. //
  158. // Initialize the monitor list head
  159. //
  160. InitializeListHead ( &g_leFmpMonitorListHead );
  161. // 185575: remove unique RPC binding handles
  162. // InitializeCriticalSection( &FmpBindingLock );
  163. // initialize the quorum lock
  164. // This is used to synchronize online/offlines of other resources
  165. // with respect to the quorum resource
  166. INITIALIZE_LOCK(gQuoLock);
  167. //this is used to check/change the resource->quorum value
  168. //This synchronization is needed between the resource transition
  169. //processing that needs to do special processing for quorum
  170. //resource and the gum update handler to change the quorum resource
  171. INITIALIZE_LOCK(gQuoChangeLock);
  172. //Initialize the restype lock
  173. INITIALIZE_LOCK(gResTypeLock);
  174. // create a unnamed event that is used for waiting for quorum resource
  175. // to go online
  176. // This is a manual reset event and is initialized to unsignalled state.
  177. // When the quorum resource goes to pending state this is manually reset
  178. // to unsignalled state. When the quorum resource goes online it is set
  179. // to signalled state
  180. ghQuoOnlineEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  181. if (!ghQuoOnlineEvent)
  182. {
  183. CL_UNEXPECTED_ERROR((Status = GetLastError()));
  184. return(Status);
  185. }
  186. gFmpNodeArray = (PFM_NODE)LocalAlloc(LMEM_FIXED,
  187. (sizeof(FM_NODE) * (NmGetMaxNodeId() + 1))
  188. );
  189. if (gFmpNodeArray == NULL) {
  190. Status = ERROR_NOT_ENOUGH_MEMORY;
  191. CL_UNEXPECTED_ERROR(Status);
  192. CsInconsistencyHalt(Status);
  193. return(Status);
  194. }
  195. //initialize it and the RPC binding table
  196. for (NodeId = ClusterMinNodeId; NodeId <= NmMaxNodeId; ++NodeId)
  197. {
  198. FmpRpcBindings[NodeId] = NULL;
  199. FmpRpcQuorumBindings[NodeId] = NULL;
  200. gFmpNodeArray[NodeId].dwNodeDownProcessingInProgress = 0;
  201. gFmpNodeArray[NodeId].dwNodeDownProcessingThreadId = 0;
  202. }
  203. //
  204. // Initialize the FM work queue.
  205. //
  206. Status = ClRtlInitializeQueue( &FmpWorkQueue );
  207. if (Status != ERROR_SUCCESS) {
  208. CsInconsistencyHalt(Status);
  209. return(Status);
  210. }
  211. //
  212. // Create a pending event notification.
  213. //
  214. FmpShutdownEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
  215. if ( FmpShutdownEvent == NULL ) {
  216. return(GetLastError());
  217. }
  218. //
  219. // Initialize Group Types.
  220. //
  221. ObjectTypeInit.Name = FMP_GROUP_NAME;
  222. ObjectTypeInit.Signature = FMP_GROUP_SIGNATURE;
  223. ObjectTypeInit.ObjectSize = sizeof(FM_GROUP);
  224. ObjectTypeInit.DeleteObjectMethod = FmpGroupLastReference;
  225. Status = OmCreateType( ObjectTypeGroup,
  226. &ObjectTypeInit );
  227. if ( Status != ERROR_SUCCESS ) {
  228. CsInconsistencyHalt(Status);
  229. return(Status);
  230. }
  231. //
  232. // Initialize Resource Types.
  233. //
  234. ObjectTypeInit.Name = FMP_RESOURCE_NAME;
  235. ObjectTypeInit.Signature = FMP_RESOURCE_SIGNATURE;
  236. ObjectTypeInit.ObjectSize = sizeof(FM_RESOURCE);
  237. ObjectTypeInit.DeleteObjectMethod = FmpResourceLastReference;
  238. Status = OmCreateType( ObjectTypeResource,
  239. &ObjectTypeInit );
  240. if ( Status != ERROR_SUCCESS ) {
  241. CsInconsistencyHalt(Status);
  242. return(Status);
  243. }
  244. //
  245. // Initialize ResType Types.
  246. //
  247. ObjectTypeInit.Name = FMP_RESOURCE_TYPE_NAME;
  248. ObjectTypeInit.Signature = FMP_RESOURCE_TYPE_SIGNATURE;
  249. ObjectTypeInit.ObjectSize = sizeof(FM_RESTYPE);
  250. ObjectTypeInit.DeleteObjectMethod = FmpResTypeLastRef;
  251. Status = OmCreateType( ObjectTypeResType,
  252. &ObjectTypeInit );
  253. if ( Status != ERROR_SUCCESS ) {
  254. CsInconsistencyHalt(Status);
  255. return(Status);
  256. }
  257. //
  258. // Initialize the Notify thread.
  259. //
  260. Status = FmpInitializeNotify();
  261. if (Status != ERROR_SUCCESS) {
  262. CsInconsistencyHalt(Status);
  263. return(Status);
  264. }
  265. //
  266. // Initialize the FM worker thread.
  267. //
  268. Status = FmpStartWorkerThread();
  269. if ( Status != ERROR_SUCCESS ) {
  270. CsInconsistencyHalt(Status);
  271. return(Status);
  272. }
  273. FmpInitialized = TRUE;
  274. return(ERROR_SUCCESS);
  275. } // FmInitialize
  276. BOOL
  277. FmpEnumGroupsInit(
  278. IN PVOID Context1,
  279. IN PVOID Context2,
  280. IN PFM_GROUP Group,
  281. IN LPCWSTR Name
  282. )
  283. /*++
  284. Routine Description:
  285. Group enumeration callback for FM join. This phase completes initialization
  286. of every group.
  287. Arguments:
  288. Context1 - Not used.
  289. Context2 - Not used.
  290. Group - Supplies the group.
  291. Name - Supplies the group's name.
  292. Return Value:
  293. TRUE - to indicate that the enumeration should continue.
  294. FALSE - to indicate that the enumeration should not continue.
  295. --*/
  296. {
  297. //
  298. // Finish initializing the group.
  299. //
  300. FmpCompleteInitGroup( Group );
  301. return(TRUE);
  302. } // FmpEnumGroupsInit
  303. BOOL
  304. FmpEnumFixupResources(
  305. IN PCLUSTERVERSIONINFO pClusterVersionInfo,
  306. IN PVOID Context2,
  307. IN PFM_GROUP Group,
  308. IN LPCWSTR Name
  309. )
  310. /*++
  311. Routine Description:
  312. Group enumeration callback for FM join. This phase completes initialization
  313. of every group.
  314. Arguments:
  315. Context1 - Not used.
  316. Context2 - Not used.
  317. Group - Supplies the group.
  318. Name - Supplies the group's name.
  319. Return Value:
  320. TRUE - to indicate that the enumeration should continue.
  321. FALSE - to indicate that the enumeration should not continue.
  322. --*/
  323. {
  324. PLIST_ENTRY listEntry;
  325. PFM_RESOURCE Resource;
  326. FmpAcquireLocalGroupLock( Group );
  327. //
  328. // For each resource in the Group, make sure it gets an
  329. // opportunity to do fixups.
  330. //
  331. for ( listEntry = Group->Contains.Flink;
  332. listEntry != &(Group->Contains);
  333. listEntry = listEntry->Flink ) {
  334. Resource = CONTAINING_RECORD(listEntry, FM_RESOURCE, ContainsLinkage);
  335. FmpRmResourceControl( Resource,
  336. CLUSCTL_RESOURCE_CLUSTER_VERSION_CHANGED,
  337. (LPBYTE)pClusterVersionInfo,
  338. pClusterVersionInfo->dwVersionInfoSize,
  339. NULL,
  340. 0,
  341. NULL,
  342. NULL
  343. );
  344. }
  345. FmpReleaseLocalGroupLock( Group);
  346. return(TRUE);
  347. } // FmpEnumFixupResources
  348. BOOL
  349. FmpEnumJoinGroupsMove(
  350. IN LPBOOL Deferred,
  351. IN PVOID Context2,
  352. IN PFM_GROUP Group,
  353. IN LPCWSTR Name
  354. )
  355. /*++
  356. Routine Description:
  357. Group enumeration callback for FM join. Queries the preferred owners
  358. groups and moves those that belong on this system and that can move.
  359. Arguments:
  360. Deferred - TRUE if a move was deferred because of Failback Window. Must
  361. be FALSE on first call.
  362. Context2 - Not used.
  363. Group - Supplies the group.
  364. Name - Supplies the group's name.
  365. Return Value:
  366. TRUE - to indicate that the enumeration should continue.
  367. FALSE - to indicate that the enumeration should not continue.
  368. --*/
  369. {
  370. PLIST_ENTRY listEntry;
  371. PPREFERRED_ENTRY preferredEntry;
  372. SYSTEMTIME localTime;
  373. BOOL failBackWindowOkay = FALSE;
  374. DWORD threadId;
  375. DWORD status;
  376. GetLocalTime( &localTime );
  377. FmpAcquireLocalGroupLock( Group );
  378. //
  379. // Adjust ending time if needed.
  380. //
  381. if ( Group->FailbackWindowStart > Group->FailbackWindowEnd ) {
  382. Group->FailbackWindowEnd += 24;
  383. if ( Group->FailbackWindowStart > localTime.wHour ) {
  384. localTime.wHour += 24;
  385. }
  386. }
  387. //
  388. // If the Failback start and end times are valid, then check if we need
  389. // to start a timer thread to move the group at the appropriate time.
  390. //
  391. if ( (Group->FailbackType == GroupFailback) &&
  392. ((Group->FailbackWindowStart != Group->FailbackWindowEnd) &&
  393. (localTime.wHour >= Group->FailbackWindowStart) &&
  394. (localTime.wHour < Group->FailbackWindowEnd)) ||
  395. (Group->FailbackWindowStart == Group->FailbackWindowEnd) ) {
  396. failBackWindowOkay = TRUE;
  397. }
  398. //
  399. // Check if we need to move the group.
  400. //
  401. if ( !IsListEmpty( &Group->PreferredOwners ) ) {
  402. listEntry = Group->PreferredOwners.Flink;
  403. preferredEntry = CONTAINING_RECORD( listEntry,
  404. PREFERRED_ENTRY,
  405. PreferredLinkage );
  406. //
  407. // Move group if:
  408. // 0. Remote system is paused, and we're not OR
  409. // 1. Our system is in the preferred list and the owner node is not OR
  410. // 2. Group is Offline or Group is Online/PartialOnline and it can
  411. // failback AND
  412. // 3. Group's preferred list is ordered and our system is higher
  413. //
  414. if ( Group->OwnerNode == NULL ) {
  415. // Should we shoot ourselves because we got an incomplete snapshot
  416. // of the joint attempt.
  417. CsInconsistencyHalt(ERROR_CLUSTER_JOIN_ABORTED);
  418. } else if ( Group->OwnerNode != NmLocalNode) {
  419. if (((NmGetNodeState(NmLocalNode) != ClusterNodePaused) &&
  420. (NmGetNodeState(Group->OwnerNode) == ClusterNodePaused)) ||
  421. (FmpInPreferredList(Group, NmLocalNode, FALSE, NULL) &&
  422. !FmpInPreferredList( Group, Group->OwnerNode, FALSE, NULL)) ||
  423. ((((Group->State == ClusterGroupOnline) ||
  424. (Group->State == ClusterGroupPartialOnline)) &&
  425. (Group->FailbackType == FailbackOkay) ||
  426. (Group->State == ClusterGroupOffline)) &&
  427. ((Group->OrderedOwners) &&
  428. (FmpHigherInPreferredList(Group, NmLocalNode, Group->OwnerNode)))) ) {
  429. if ( failBackWindowOkay ) {
  430. PNM_NODE OwnerNode = Group->OwnerNode;
  431. status = FmcMoveGroupRequest( Group, NmLocalNode );
  432. if ( ( status == ERROR_SUCCESS ) || ( status == ERROR_IO_PENDING ) ) {
  433. //
  434. // Chittur Subbaraman (chitturs) - 7/31/2000
  435. //
  436. // Log an event indicating an impending failback.
  437. //
  438. CsLogEvent3( LOG_NOISE,
  439. FM_EVENT_GROUP_FAILBACK,
  440. OmObjectName(Group),
  441. OmObjectName(OwnerNode),
  442. OmObjectName(NmLocalNode) );
  443. }
  444. FmpAcquireLocalGroupLock( Group );
  445. } else {
  446. //
  447. // Start timer thread if not already running. If it fails,
  448. // what possibly can we do?
  449. //
  450. if ( FmpTimerThread == NULL ) {
  451. FmpTimerThread = CreateThread( NULL,
  452. 0,
  453. FmpJoinPendingThread,
  454. NULL,
  455. 0,
  456. &threadId );
  457. }
  458. *Deferred = TRUE;
  459. }
  460. }
  461. }
  462. }
  463. FmpReleaseLocalGroupLock( Group );
  464. return(TRUE);
  465. } // FmpEnumJoinGroups
  466. BOOL
  467. FmpEnumSignalGroups(
  468. IN PVOID Context1,
  469. IN PVOID Context2,
  470. IN PFM_GROUP Group,
  471. IN LPCWSTR Name
  472. )
  473. /*++
  474. Routine Description:
  475. Group enumeration callback to indicate state change on all groups
  476. and resources.
  477. For the quorum resource, if we're forming a cluster, we'll also
  478. fixup information that was not available when the resource was created.
  479. Arguments:
  480. Context1 - Pointer to a BOOL that is TRUE if this is a FormCluster.
  481. FALSE otherwise.
  482. Context2 - Not used.
  483. Group - Supplies the group.
  484. Name - Supplies the group's name.
  485. Return Value:
  486. TRUE - to indicate that the enumeration should continue.
  487. FALSE - to indicate that the enumeration should not continue.
  488. --*/
  489. {
  490. PLIST_ENTRY listEntry;
  491. PFM_RESOURCE resource;
  492. BOOL formCluster = *(PBOOL)Context1;
  493. DWORD status;
  494. BOOL quorumGroup = FALSE;
  495. //
  496. // For each resource in the group, generate an event notification.
  497. //
  498. for (listEntry = Group->Contains.Flink;
  499. listEntry != &(Group->Contains);
  500. listEntry = listEntry->Flink ) {
  501. resource = CONTAINING_RECORD( listEntry,
  502. FM_RESOURCE,
  503. ContainsLinkage );
  504. //
  505. // If this is the quorum resource and we're performing a Form
  506. // Cluster, then fixup the quorum resource info.
  507. //
  508. if ( resource->QuorumResource ) {
  509. status = FmpFixupResourceInfo( resource );
  510. quorumGroup = TRUE;
  511. if ( status != ERROR_SUCCESS ) {
  512. ClRtlLogPrint( LOG_NOISE,
  513. "[FM] Warning, failed to fixup quorum resource %1!ws!, error %2!u!.\n",
  514. OmObjectId(resource),
  515. status );
  516. }
  517. }
  518. if ( resource->State == ClusterResourceOnline ) {
  519. ClusterEvent( CLUSTER_EVENT_RESOURCE_ONLINE, resource );
  520. } else {
  521. ClusterEvent( CLUSTER_EVENT_RESOURCE_OFFLINE, resource );
  522. }
  523. }
  524. if ( quorumGroup ) {
  525. status = FmpFixupGroupInfo( Group );
  526. if ( status != ERROR_SUCCESS ) {
  527. ClRtlLogPrint( LOG_NOISE,
  528. "[FM] Warning, failed to fixup quorum group %1!ws!, error %2!u!.\n",
  529. OmObjectId( Group ),
  530. status );
  531. }
  532. }
  533. if ( Group->State == ClusterGroupOnline ) {
  534. ClusterEvent( CLUSTER_EVENT_GROUP_ONLINE, Group );
  535. } else {
  536. ClusterEvent( CLUSTER_EVENT_GROUP_OFFLINE, Group );
  537. }
  538. return(TRUE);
  539. } // FmpEnumSignalGroups
  540. DWORD
  541. FmpJoinPendingThread(
  542. IN LPVOID Context
  543. )
  544. /*++
  545. Routine Description:
  546. Thread to keep trying to move groups, as long we are blocked by a
  547. FailbackWindow problem. This thread runs every 15 minutes to attempt to
  548. move Groups.
  549. Arguments:
  550. Context - Not used.
  551. Return Value:
  552. ERROR_SUCCESS.
  553. --*/
  554. {
  555. DWORD status;
  556. BOOL deferred;
  557. //
  558. // As long as we have deferred Group moves, keep going.
  559. do {
  560. status = WaitForSingleObject( FmpShutdownEvent, WINDOW_TIMEOUT );
  561. if ( FmpShutdown ) {
  562. goto finished;
  563. }
  564. deferred = FALSE;
  565. //
  566. // For each group, see if it should be moved to the local system.
  567. //
  568. OmEnumObjects( ObjectTypeGroup,
  569. FmpEnumJoinGroupsMove,
  570. &deferred,
  571. NULL );
  572. } while ( (status != WAIT_FAILED) && deferred );
  573. finished:
  574. CloseHandle( FmpTimerThread );
  575. FmpTimerThread = NULL;
  576. return(ERROR_SUCCESS);
  577. } // FmpJoinPendingThread
  578. DWORD
  579. WINAPI
  580. FmGetQuorumResource(
  581. OUT PFM_GROUP *ppQuoGroup,
  582. OUT LPDWORD lpdwSignature OPTIONAL
  583. )
  584. /*++
  585. Routine Description:
  586. Find the quorum resource, arbitrate it and return a name that can be
  587. used to open the device in order to perform reads. Optionally,
  588. return the signature of the quorum disk.
  589. There are 3 items that we need:
  590. 1. The name of the quorum resource.
  591. 2. The name of the Group that the quorum resource is a member of.
  592. 3. The resource type for the quorum resource.
  593. Arguments:
  594. ppQuoGroup - Supplies a pointer to a buffer into which the
  595. quorum group info is returned.
  596. lpdwSignature - An optional argument which is used to return
  597. the signature of the quorum disk from the cluster hive.
  598. Return Value:
  599. ERROR_SUCCESS if successful.
  600. A Win32 error code on failure.
  601. --*/
  602. {
  603. LPWSTR quorumId = NULL;
  604. LPWSTR groupId = NULL;
  605. LPCWSTR stringId;
  606. LPWSTR containsString = NULL;
  607. PFM_GROUP group = NULL;
  608. PFM_RESOURCE resource = NULL;
  609. HDMKEY hGroupKey;
  610. DWORD groupIdSize = 0;
  611. DWORD idMaxSize = 0;
  612. DWORD idSize = 0;
  613. DWORD status;
  614. DWORD keyIndex;
  615. DWORD stringIndex;
  616. *ppQuoGroup = NULL;
  617. //
  618. // Get the quorum resource value.
  619. //
  620. status = DmQuerySz( DmQuorumKey,
  621. CLUSREG_NAME_QUORUM_RESOURCE,
  622. (LPWSTR*)&quorumId,
  623. &idMaxSize,
  624. &idSize );
  625. if ( status != ERROR_SUCCESS ) {
  626. ClRtlLogPrint(LOG_CRITICAL,
  627. "[FM] Failed to get quorum resource, error %1!u!.\n",
  628. status);
  629. goto FnExit;
  630. }
  631. //
  632. // Chittur Subbaraman (chitturs) - 10/30/98
  633. //
  634. // If the user is forcing a database restore operation, you
  635. // also need to verify whether the quorum disk signature in
  636. // the registry matches that in the disk itself. So, go get
  637. // the signature from the Cluster\Resources\quorumId\Parameters
  638. // key
  639. //
  640. if ( lpdwSignature != NULL ) {
  641. status = FmpGetQuorumDiskSignature( quorumId, lpdwSignature );
  642. if ( status != ERROR_SUCCESS ) {
  643. //
  644. // This is not a fatal error. So log an error and go on.
  645. //
  646. ClRtlLogPrint(LOG_CRITICAL,
  647. "[FM] Failed to get quorum disk signature, error %1!u!.\n",
  648. status);
  649. }
  650. }
  651. //
  652. // Initialize the default Resource Monitor
  653. //
  654. if ( FmpDefaultMonitor == NULL ) {
  655. FmpDefaultMonitor = FmpCreateMonitor(NULL, FALSE);
  656. }
  657. if (FmpDefaultMonitor == NULL) {
  658. status = GetLastError();
  659. CsInconsistencyHalt(status);
  660. goto FnExit;
  661. }
  662. //
  663. // Now find the group that the quorum resource is a member of.
  664. //
  665. idMaxSize = 0;
  666. idSize = 0;
  667. for ( keyIndex = 0; ; keyIndex++ )
  668. {
  669. status = FmpRegEnumerateKey( DmGroupsKey,
  670. keyIndex,
  671. &groupId,
  672. &groupIdSize );
  673. if ( status != ERROR_SUCCESS )
  674. {
  675. ClRtlLogPrint(LOG_CRITICAL, "[FM] FmGetQuorumResource: FmpRegEnumerateKey returns %1!u!\n",
  676. status);
  677. break;
  678. }
  679. //open the group key
  680. hGroupKey = DmOpenKey( DmGroupsKey,
  681. groupId,
  682. KEY_READ );
  683. if (!hGroupKey)
  684. continue;
  685. //
  686. // Get the contains string.
  687. //
  688. status = DmQueryMultiSz( hGroupKey,
  689. CLUSREG_NAME_GRP_CONTAINS,
  690. &containsString,
  691. &idMaxSize,
  692. &idSize );
  693. DmCloseKey(hGroupKey);
  694. if ( status != ERROR_SUCCESS )
  695. continue;
  696. for ( stringIndex = 0; ; stringIndex++ )
  697. {
  698. stringId = ClRtlMultiSzEnum( containsString,
  699. idSize/sizeof(WCHAR),
  700. stringIndex );
  701. if ( stringId == NULL ) {
  702. break;
  703. }
  704. if ( lstrcmpiW( stringId, quorumId ) == 0 )
  705. {
  706. // We will now create the group, which will also
  707. // create the resource, and the resource type.
  708. //
  709. // TODO - this will also create all resources
  710. // within the group. What should we do about that?
  711. // We could require the quorum resource to be in
  712. // a group by itself! (rodga) 17-June-1996.
  713. //
  714. group = FmpCreateGroup( groupId,
  715. FALSE );
  716. if (CsNoQuorum)
  717. FmpSetGroupPersistentState(group, ClusterGroupOffline);
  718. break;
  719. }
  720. }
  721. //if we found the group, thre is no need to search for more
  722. if (group != NULL)
  723. break;
  724. }
  725. //
  726. // Check if we found the Quorum resource's group.
  727. //
  728. if ( group == NULL )
  729. {
  730. ClRtlLogPrint(LOG_NOISE,
  731. "[FM] Did not find group for quorum resource.\n");
  732. status = ERROR_GROUP_NOT_FOUND;
  733. goto FnExit;
  734. }
  735. //
  736. // Get the quorum resource structure.
  737. //
  738. resource = OmReferenceObjectById( ObjectTypeResource, quorumId );
  739. if ( resource == NULL )
  740. {
  741. ClRtlLogPrint(LOG_CRITICAL,
  742. "[FM] Failed to find quorum resource object.\n");
  743. status = ERROR_RESOURCE_NOT_FOUND;
  744. goto FnExit;
  745. }
  746. resource->QuorumResource = TRUE;
  747. if (!CsNoQuorum)
  748. {
  749. ClRtlLogPrint(LOG_CRITICAL,
  750. "[FM] Arbitrate for quorum resource id %1!ws!.\n",
  751. OmObjectId(resource));
  752. //
  753. // First finish initializing the quorum resource.
  754. //
  755. if ( resource->Monitor == NULL )
  756. {
  757. status = FmpInitializeResource( resource, TRUE );
  758. if ( status != ERROR_SUCCESS )
  759. {
  760. ClRtlLogPrint(LOG_UNUSUAL,
  761. "[FM] Error completing initialization of quorum resource '%1!ws!, error %2!u!.\n",
  762. OmObjectId(resource),
  763. status );
  764. goto FnExit;
  765. }
  766. }
  767. if ( CsForceQuorum ) {
  768. status = FmpSendForceQuorumControlToResource( resource );
  769. if ( status != ERROR_SUCCESS ) {
  770. // The routine does its own logging. Just bail.
  771. goto FnExit;
  772. }
  773. }
  774. //
  775. // Now arbitrate for the resource.
  776. //
  777. status = FmpRmArbitrateResource( resource );
  778. }
  779. FnExit:
  780. if ( status == ERROR_SUCCESS ) {
  781. ClRtlLogPrint(LOG_NOISE,
  782. "[FM] FmGetQuorumResource successful\n");
  783. *ppQuoGroup = group;
  784. }
  785. else
  786. {
  787. ClRtlLogPrint(LOG_CRITICAL,
  788. "[FM] FmGetQuorumResource failed, error %1!u!.\n",
  789. status);
  790. //the group will be cleaned by fmshutdown()
  791. }
  792. if (resource) OmDereferenceObject(resource);
  793. if (quorumId) LocalFree(quorumId);
  794. if (groupId) LocalFree(groupId);
  795. //
  796. // Chittur Subbaraman (chitturs) - 10/05/98
  797. // Fix memory leak
  798. //
  799. if (containsString) LocalFree(containsString);
  800. return(status);
  801. } // FmGetQuorumResource
  802. DWORD
  803. WINAPI
  804. FmpSendForceQuorumControlToResource(
  805. PFM_RESOURCE resource )
  806. {
  807. PCLUS_FORCE_QUORUM_INFO pForceQuorumInfo = NULL;
  808. DWORD status;
  809. //
  810. // If we have a force quorum (Majority Node Set) then drop a control code to the
  811. // resource with the list of nodes. This must be done before
  812. // arbitrate. First we build force quorum info - this makes sure that the node list is valid etc.
  813. // Note that the list can be NULL.
  814. //
  815. status = FmpBuildForceQuorumInfo( CsForceQuorumNodes,
  816. &pForceQuorumInfo );
  817. if ( status != ERROR_SUCCESS ) {
  818. ClRtlLogPrint(LOG_UNUSUAL,
  819. "[FM] Error building force quorum info for resource '%1!ws!, error %2!u!.\n",
  820. OmObjectId(resource),
  821. status );
  822. goto FnExit;
  823. }
  824. ClRtlLogPrint(LOG_NOISE,
  825. "[FM] sending CLUSCTL_RESOURCE_FORCE_QUORUM\n" );
  826. status = FmpRmResourceControl( resource,
  827. CLUSCTL_RESOURCE_FORCE_QUORUM,
  828. (LPBYTE)pForceQuorumInfo,
  829. pForceQuorumInfo->dwSize,
  830. NULL,
  831. 0,
  832. NULL,
  833. NULL );
  834. //
  835. // Tolerate ERROR_INVALID_FUNCTION since this just means that the
  836. // resource doesn't handle it.
  837. //
  838. if ( status == ERROR_INVALID_FUNCTION )
  839. status = ERROR_SUCCESS;
  840. if ( status != ERROR_SUCCESS ) {
  841. ClRtlLogPrint(LOG_CRITICAL,
  842. "[FM] Resource control for Force Quorum for resource '%1!ws! encountered error %2!u!.\n",
  843. OmObjectId(resource),
  844. status );
  845. }
  846. FnExit:
  847. if (pForceQuorumInfo) FmpDeleteForceQuorumInfo( &pForceQuorumInfo );
  848. return status;
  849. }
  850. BOOL
  851. WINAPI
  852. FmpIsNodeInForceQuorumNodes(
  853. IN LPCWSTR lpszNodeId )
  854. {
  855. BOOL result = FALSE;
  856. PCLUS_FORCE_QUORUM_INFO pForceQuorumInfo = NULL;
  857. DWORD dwNodeId;
  858. PNM_NODE pNmNode = NULL;
  859. DWORD status;
  860. status = FmpBuildForceQuorumInfo( CsForceQuorumNodes,
  861. &pForceQuorumInfo );
  862. if ( status != ERROR_SUCCESS ) {
  863. ClRtlLogPrint(LOG_UNUSUAL,
  864. "[FM] Error building force quorum info, error %1!u!.\n",
  865. status );
  866. goto FnExit;
  867. }
  868. pNmNode = OmReferenceObjectById(
  869. ObjectTypeNode,
  870. lpszNodeId );
  871. if (pNmNode == NULL) {
  872. status = ERROR_CLUSTER_NODE_NOT_MEMBER;
  873. ClRtlLogPrint( LOG_UNUSUAL,
  874. "[FM] Node %1!ws! is not a member of this cluster. Cannot join.\n",
  875. lpszNodeId );
  876. goto FnExit;
  877. }
  878. dwNodeId = NmGetNodeId( pNmNode );
  879. result = ( pForceQuorumInfo->dwNodeBitMask & ( 1 << dwNodeId )) != 0;
  880. ClRtlLogPrint( LOG_NOISE,
  881. "[FM] Node %1!ws! is %2!ws!in the ForceQuorumNodes list.\n",
  882. lpszNodeId,
  883. ( result ? L"" : L"not " ));
  884. FnExit:
  885. if (pForceQuorumInfo) FmpDeleteForceQuorumInfo( &pForceQuorumInfo );
  886. if ( pNmNode ) OmDereferenceObject( pNmNode );
  887. return result;
  888. }
  889. DWORD
  890. WINAPI
  891. FmFindQuorumResource(
  892. OUT PFM_RESOURCE *ppResource
  893. )
  894. /*++
  895. Routine Description:
  896. Finds the quorum resource and returns a pointer to the resource
  897. object.
  898. Arguments:
  899. *ppResource - A pointer to the Quorum resource object is returned in this.
  900. Return Value:
  901. ERROR_SUCCESS if successful.
  902. A Win32 error code on failure.
  903. --*/
  904. {
  905. DWORD dwError = ERROR_SUCCESS;
  906. //enumerate all the resources
  907. *ppResource = NULL;
  908. OmEnumObjects( ObjectTypeResource,
  909. FmpFindQuorumResource,
  910. ppResource,
  911. NULL );
  912. if ( *ppResource == NULL )
  913. {
  914. dwError = ERROR_RESOURCE_NOT_FOUND;
  915. CL_LOGCLUSERROR(FM_QUORUM_RESOURCE_NOT_FOUND);
  916. }
  917. return(dwError);
  918. }
  919. DWORD WINAPI FmFindQuorumOwnerNodeId(IN PFM_RESOURCE pResource)
  920. {
  921. DWORD dwNodeId;
  922. CL_ASSERT(pResource->Group->OwnerNode != NULL);
  923. dwNodeId = NmGetNodeId(pResource->Group->OwnerNode);
  924. return (dwNodeId);
  925. }
  926. BOOL
  927. FmpReturnResourceType(
  928. IN OUT PFM_RESTYPE *FoundResourceType,
  929. IN LPCWSTR ResourceTypeName,
  930. IN PFM_RESTYPE ResourceType,
  931. IN LPCWSTR Name
  932. )
  933. /*++
  934. Routine Description:
  935. Group enumeration callback for FM join. Queries the preferred owners
  936. groups and moves those that belong on this system and that can move.
  937. Arguments:
  938. ResourceType - Returns the found ResourceType, if found.
  939. Context2 - The input resource type name to find.
  940. Resource - Supplies the current ResourceType.
  941. Name - Supplies the ResourceType's name.
  942. Return Value:
  943. TRUE - to indicate that the enumeration should continue.
  944. FALSE - to indicate that the enumeration should not continue.
  945. --*/
  946. {
  947. if ( lstrcmpiW( Name, ResourceTypeName ) == 0 ) {
  948. OmReferenceObject( ResourceType );
  949. *FoundResourceType = ResourceType;
  950. return(FALSE);
  951. }
  952. return(TRUE);
  953. } // FmpReturnResourceType
  954. DWORD
  955. WINAPI
  956. FmFormNewClusterPhase1(
  957. IN PFM_GROUP pQuoGroup
  958. )
  959. /*++
  960. Routine Description:
  961. Destroys the quorum group that was created. The quorum resource is left
  962. behind and its group adjusted according to the new logs.
  963. Arguments:
  964. None.
  965. Returns:
  966. ERROR_SUCCESS if successful
  967. Win32 errorcode otherwise.
  968. --*/
  969. {
  970. DWORD status;
  971. ClRtlLogPrint(LOG_NOISE,
  972. "[FM] FmFormNewClusterPhase1, Entry. Quorum quorum will be deleted\n");
  973. //
  974. // Enable the GUM.
  975. //
  976. GumReceiveUpdates(FALSE,
  977. GumUpdateFailoverManager,
  978. FmpGumReceiveUpdates,
  979. NULL,
  980. sizeof(FmGumDispatchTable)/sizeof(GUM_DISPATCH_ENTRY),
  981. FmGumDispatchTable,
  982. FmpGumVoteHandler);
  983. //Acquire the exclusive lock for the quorum
  984. // This is done so that we can ignore any resource transition events from
  985. // the quorum resource between phase 1 and phase 2 of FM initialization on Form
  986. ACQUIRE_EXCLUSIVE_LOCK(gQuoChangeLock);
  987. FmpFMFormPhaseProcessing = TRUE;
  988. //release the quorum lock
  989. RELEASE_LOCK(gQuoChangeLock);
  990. //the group lock will be freed by FmpDestroyGroup
  991. FmpAcquireLocalGroupLock( pQuoGroup );
  992. //destroy the quorum group object, dont bring the quorum resource online/offline
  993. //All resources in the quorum group must get deleted, except the quorum resource
  994. //All resources in the quorum group must get recreated in FmFormNewClusterPhase2.
  995. //The quorum group is removed from the group list, hence it will be recreated in phase2.
  996. //Since the quorum resource must not get deleted we will increment its ref count
  997. //This is because in phase 2 it is not created and its ref count is not incremented at create
  998. //By the time it is put on the contains list, we expect the resource count to be 2.
  999. OmReferenceObject(gpQuoResource);
  1000. status = FmpDestroyGroup(pQuoGroup, TRUE);
  1001. //We prefer that the quorum group is deleted
  1002. //since after rollback the old group may no longer exist and we
  1003. //dont want it to be on the group list
  1004. gpQuoResource->Group = NULL;
  1005. OmDereferenceObject(pQuoGroup);
  1006. return(status);
  1007. } // FmFormNewClusterPhase1
  1008. DWORD
  1009. WINAPI
  1010. FmFormNewClusterPhase2(
  1011. VOID
  1012. )
  1013. /*++
  1014. Routine Description:
  1015. Bring the Failover Manager Online, this means claiming all groups and
  1016. finishing the initialization of resources.
  1017. Arguments:
  1018. None.
  1019. Returns:
  1020. ERROR_SUCCESS if successful
  1021. Win32 errorcode otherwise.
  1022. --*/
  1023. {
  1024. DWORD status;
  1025. BOOL formCluster = TRUE;
  1026. PFM_GROUP group;
  1027. PFM_RESOURCE pQuoResource=NULL;
  1028. CLUSTERVERSIONINFO ClusterVersionInfo;
  1029. PCLUSTERVERSIONINFO pClusterVersionInfo = NULL;
  1030. PGROUP_ENUM MyGroups = NULL;
  1031. BOOL QuorumGroup;
  1032. ClRtlLogPrint(LOG_NOISE,
  1033. "[FM] FmFormNewClusterPhase2, Entry.\n");
  1034. //
  1035. // Initialize resource types
  1036. //
  1037. status = FmpInitResourceTypes();
  1038. if (status != ERROR_SUCCESS) {
  1039. CsInconsistencyHalt(status);
  1040. goto error_exit;
  1041. }
  1042. //
  1043. // Initialize Groups,
  1044. //
  1045. status = FmpInitGroups( FALSE );
  1046. if (status != ERROR_SUCCESS) {
  1047. goto error_exit;
  1048. }
  1049. // refigure out the state for the quorum group
  1050. status = FmFindQuorumResource(&pQuoResource);
  1051. if (status != ERROR_SUCCESS)
  1052. {
  1053. goto error_exit;
  1054. }
  1055. //
  1056. // Set the state of the quorum group depending upon the state of
  1057. // the quorum resource
  1058. //
  1059. //now we should enable resource events to come in for the quorum resource as well
  1060. ACQUIRE_EXCLUSIVE_LOCK(gQuoChangeLock);
  1061. FmpFMFormPhaseProcessing = FALSE;
  1062. group = pQuoResource->Group;
  1063. group->State = FmpGetGroupState(group, TRUE);
  1064. OmDereferenceObject(pQuoResource);
  1065. //if the noquorum flag is set, dont bring the quorum group online
  1066. if (CsNoQuorum)
  1067. FmpSetGroupPersistentState(pQuoResource->Group, ClusterGroupOffline);
  1068. RELEASE_LOCK(gQuoChangeLock);
  1069. //
  1070. // Check if resource dll deadlock detection is enabled. This must be called only
  1071. // after FmpInitialized is set to TRUE.
  1072. //
  1073. FmCheckIsDeadlockDetectionEnabled ();
  1074. //
  1075. // Initialize the default Resource Monitor
  1076. //
  1077. if ( FmpDefaultMonitor == NULL ) {
  1078. FmpDefaultMonitor = FmpCreateMonitor(NULL, FALSE);
  1079. }
  1080. if (FmpDefaultMonitor == NULL) {
  1081. status = GetLastError();
  1082. ClRtlLogPrint(LOG_CRITICAL,
  1083. "[FM] Failed to create default resource monitor on Form.\n");
  1084. goto error_exit;
  1085. }
  1086. if (NmLocalNodeVersionChanged)
  1087. {
  1088. //initialize the version information
  1089. CsGetClusterVersionInfo(&ClusterVersionInfo);
  1090. pClusterVersionInfo = &ClusterVersionInfo;
  1091. }
  1092. //enable votes and gum updates since the fixups for
  1093. //resource types require that
  1094. FmpFMGroupsInited = TRUE;
  1095. //
  1096. // The resource type possible node list is built
  1097. // using a voting protocol, hence we need to
  1098. // fix it up since the vote could have been conducted
  1099. // while this node was down.
  1100. // Also call the resource type control code if the
  1101. // local node version has changed
  1102. //
  1103. status = FmpFixupResourceTypesPhase1(FALSE, NmLocalNodeVersionChanged,
  1104. pClusterVersionInfo);
  1105. if (status != ERROR_SUCCESS) {
  1106. CsInconsistencyHalt(status);
  1107. goto error_exit;
  1108. }
  1109. //
  1110. // Find and sort all known groups
  1111. //
  1112. status = FmpEnumSortGroups(&MyGroups, NULL, &QuorumGroup);
  1113. if (status != ERROR_SUCCESS) {
  1114. goto error_exit;
  1115. }
  1116. //
  1117. // Find the state of the Groups.
  1118. //
  1119. FmpGetGroupListState( MyGroups );
  1120. //
  1121. // Set the Group owner.
  1122. //
  1123. FmpSetGroupEnumOwner( MyGroups, NmLocalNode, NULL, QuorumGroup, NULL );
  1124. //
  1125. // For each group, finish initialization of all groups and resources.
  1126. //
  1127. OmEnumObjects( ObjectTypeGroup,
  1128. FmpEnumGroupsInit,
  1129. NULL,
  1130. NULL );
  1131. // if the resource type is not supported, remove it from the possible
  1132. // owners list of all resources of that type
  1133. status = FmpFixupPossibleNodesForResources(FALSE);
  1134. if (status != ERROR_SUCCESS) {
  1135. CsInconsistencyHalt(status);
  1136. return(status);
  1137. }
  1138. if (NmLocalNodeVersionChanged)
  1139. {
  1140. //
  1141. // For each group, allow all resources to do any fixups
  1142. // they might need to do to the cluster registry to
  1143. // run in a mixed mode cluster.
  1144. //
  1145. // Get the version info
  1146. OmEnumObjects( ObjectTypeGroup,
  1147. FmpEnumFixupResources,
  1148. &ClusterVersionInfo,
  1149. NULL );
  1150. }
  1151. //
  1152. // Take ownership of all the groups in the system. This also completes
  1153. // the initialization of all resources.
  1154. //
  1155. status = FmpClaimAllGroups(MyGroups);
  1156. if (status != ERROR_SUCCESS) {
  1157. ClRtlLogPrint(LOG_CRITICAL,"[FM] FmpClaimAllGroups failed %1!d!\n",status);
  1158. goto error_exit;
  1159. }
  1160. //
  1161. // Cleanup
  1162. //
  1163. FmpDeleteEnum(MyGroups);
  1164. FmpFMOnline = TRUE;
  1165. //
  1166. // Signal a state change for every group and resource!
  1167. //
  1168. OmEnumObjects( ObjectTypeGroup,
  1169. FmpEnumSignalGroups,
  1170. &formCluster,
  1171. NULL );
  1172. //
  1173. // Chittur Subbaraman (chitturs) - 5/3/2000
  1174. //
  1175. // Make sure the phase 2 notifications are delivered only after all initialization is
  1176. // complete. This includes fixing up the possible owners of the quorum resource by
  1177. // FmpEnumSignalGroups. Once phase 2 notifications are delivered, resource type DLLs
  1178. // would be free to issue cluster API calls into FM and the lack of possible owners should
  1179. // not be the reason to reject these calls.
  1180. //
  1181. status = FmpFixupResourceTypesPhase2(FALSE, NmLocalNodeVersionChanged,
  1182. pClusterVersionInfo);
  1183. if (status != ERROR_SUCCESS) {
  1184. CsInconsistencyHalt( status );
  1185. goto error_exit;
  1186. }
  1187. ClRtlLogPrint(LOG_NOISE,"[FM] FmFormNewClusterPhase2 complete.\n");
  1188. return(ERROR_SUCCESS);
  1189. error_exit:
  1190. if (MyGroups) FmpDeleteEnum(MyGroups);
  1191. FmpShutdown = TRUE;
  1192. FmpFMOnline = FALSE;
  1193. FmpCleanupGroups(FALSE);
  1194. if (FmpDefaultMonitor != NULL) {
  1195. FmpShutdownMonitor( FmpDefaultMonitor );
  1196. FmpDefaultMonitor = NULL;
  1197. }
  1198. FmpShutdown = FALSE;
  1199. return(status);
  1200. } // FmFormNewClusterPhase2
  1201. DWORD
  1202. WINAPI
  1203. FmJoinPhase1(
  1204. OUT DWORD *EndSeq
  1205. )
  1206. /*++
  1207. Routine Description:
  1208. Performs the FM initialization and join procedure. This creates skeletal
  1209. groups and resources, which are not fully initialized. After the API is
  1210. fully enabled (in Phase 2) we will finish initialization of the groups
  1211. and resources (which causes the resource monitors to run and opens
  1212. the resource DLL's.
  1213. Arguments:
  1214. None.
  1215. Return Value:
  1216. ERROR_SUCCESS if successful
  1217. Win32 errorcode otherwise.
  1218. --*/
  1219. {
  1220. DWORD status;
  1221. DWORD sequence;
  1222. int retries = 0;
  1223. //
  1224. // Enable Gum updates.
  1225. //
  1226. GumReceiveUpdates(TRUE,
  1227. GumUpdateFailoverManager,
  1228. FmpGumReceiveUpdates,
  1229. NULL,
  1230. sizeof(FmGumDispatchTable)/sizeof(GUM_DISPATCH_ENTRY),
  1231. FmGumDispatchTable,
  1232. FmpGumVoteHandler);
  1233. retry:
  1234. status = GumBeginJoinUpdate(GumUpdateFailoverManager, &sequence);
  1235. if (status != ERROR_SUCCESS) {
  1236. ClRtlLogPrint(LOG_CRITICAL,
  1237. "[FM] GumBeginJoinUpdate failed %1!d!\n",
  1238. status);
  1239. return(status);
  1240. }
  1241. //
  1242. // Build up all the FM data structures for resource types.
  1243. //
  1244. //
  1245. // Initialize resource types
  1246. //
  1247. status = FmpInitResourceTypes();
  1248. if (status != ERROR_SUCCESS) {
  1249. CsInconsistencyHalt(status);
  1250. return(status);
  1251. }
  1252. //
  1253. // Initialize Groups, but don't fully initialize them yet.
  1254. //
  1255. status = FmpInitGroups( FALSE );
  1256. if (status != ERROR_SUCCESS) {
  1257. return(status);
  1258. }
  1259. //
  1260. // Initialize the default Resource Monitor. This step must be done before end join update
  1261. // since this node can receive certain updates such as s_GumCollectVoteFromNode immediately
  1262. // after GumEndJoinUpdate which may need the services of the default monitor.
  1263. //
  1264. if ( FmpDefaultMonitor == NULL ) {
  1265. FmpDefaultMonitor = FmpCreateMonitor(NULL, FALSE);
  1266. }
  1267. if ( FmpDefaultMonitor == NULL ) {
  1268. status = GetLastError();
  1269. CsInconsistencyHalt(status);
  1270. return(status);
  1271. }
  1272. //
  1273. // Get the group and resource state from each node which is online.
  1274. //
  1275. status = ERROR_SUCCESS;
  1276. OmEnumObjects( ObjectTypeNode,
  1277. FmpEnumNodes,
  1278. &status,
  1279. NULL );
  1280. if (status == ERROR_SUCCESS) {
  1281. FmpFMGroupsInited = TRUE;
  1282. // Gum Update handlers for resource and group state changes
  1283. // can process the updates now.
  1284. status = GumEndJoinUpdate(sequence,
  1285. GumUpdateFailoverManager,
  1286. FmUpdateJoin,
  1287. 0,
  1288. NULL);
  1289. if (status == ERROR_CLUSTER_DATABASE_SEQMISMATCH) {
  1290. ClRtlLogPrint(LOG_UNUSUAL,
  1291. "[FM] GumEndJoinUpdate with sequence %1!d! failed with a sequence mismatch\n",
  1292. sequence);
  1293. } else if (status != ERROR_SUCCESS) {
  1294. ClRtlLogPrint(LOG_CRITICAL,
  1295. "[FM] GumEndJoinUpdate with sequence %1!d! failed with status %2!d!\n",
  1296. sequence,
  1297. status);
  1298. }
  1299. } else {
  1300. ClRtlLogPrint(LOG_UNUSUAL,
  1301. "[FM] FmJoin: FmpEnumNodes failed %1!d!\n",
  1302. status);
  1303. return(status);
  1304. }
  1305. if (status != ERROR_SUCCESS) {
  1306. //
  1307. // clean up resources
  1308. //
  1309. FmpShutdown = TRUE;
  1310. FmpCleanupGroups(FALSE);
  1311. FmpShutdown = FALSE;
  1312. if ( retries++ < 3 ) {
  1313. ClRtlLogPrint(LOG_UNUSUAL, "[FM] FmJoinPhase1: retry %1!d!\n", retries);
  1314. goto retry;
  1315. }
  1316. }
  1317. else {
  1318. ClRtlLogPrint(LOG_NOISE,"[FM] FmJoinPhase1 complete.\n");
  1319. // Update EndSeq on success
  1320. *EndSeq = sequence;
  1321. //
  1322. // Check if resource dll deadlock detection is enabled. This must be called only
  1323. // after FmpInitialized is set to TRUE.
  1324. //
  1325. FmCheckIsDeadlockDetectionEnabled ();
  1326. }
  1327. return(status);
  1328. } // FmJoinPhase1
  1329. DWORD
  1330. WINAPI
  1331. FmJoinPhase2(
  1332. VOID
  1333. )
  1334. /*++
  1335. Routine Description:
  1336. Performs the second phase of FM initialization and join procedure.
  1337. Finish creation of resources by allowing the resource monitors to be
  1338. created. Claim any groups which should failback to this node.
  1339. Arguments:
  1340. None.
  1341. Return Value:
  1342. ERROR_SUCCESS if successful
  1343. Win32 errorcode otherwise.
  1344. --*/
  1345. {
  1346. DWORD status;
  1347. CLUSTERVERSIONINFO ClusterVersionInfo;
  1348. PCLUSTERVERSIONINFO pClusterVersionInfo = NULL;
  1349. DWORD dwRetryCount=60;//try for atleast a minute
  1350. GetJoinApproval:
  1351. status = FmpGetJoinApproval();
  1352. if (status == ERROR_RETRY)
  1353. {
  1354. // if the other nodes have pending work to do
  1355. //after this node last died and are not willing
  1356. // to accept it back till that is over, we will stall
  1357. // the join
  1358. //sleep for a second
  1359. dwRetryCount--;
  1360. if (dwRetryCount)
  1361. {
  1362. Sleep(1000);
  1363. goto GetJoinApproval;
  1364. }
  1365. else
  1366. {
  1367. ClRtlLogPrint(LOG_CRITICAL,
  1368. "[FM] FmJoinPhase2 : timed out trying to get join approval.\n");
  1369. CsInconsistencyHalt(status);
  1370. }
  1371. }
  1372. if (NmLocalNodeVersionChanged)
  1373. {
  1374. //initialize the cluster versioninfo structure
  1375. CsGetClusterVersionInfo(&ClusterVersionInfo);
  1376. pClusterVersionInfo = &ClusterVersionInfo;
  1377. }
  1378. //
  1379. // The resource type possible node list is built
  1380. // using a voting protocol, hence we need to
  1381. // fix it up since the vote could have been conducted
  1382. // while this node was down.
  1383. //
  1384. status = FmpFixupResourceTypesPhase1(TRUE, NmLocalNodeVersionChanged,
  1385. pClusterVersionInfo);
  1386. if (status != ERROR_SUCCESS) {
  1387. CsInconsistencyHalt(status);
  1388. return(status);
  1389. }
  1390. //
  1391. // For each group, finish initialization of all groups and resources.
  1392. //
  1393. OmEnumObjects( ObjectTypeGroup,
  1394. FmpEnumGroupsInit,
  1395. NULL,
  1396. NULL );
  1397. // if the resource type is not supported, remove it from the possible
  1398. // owners list of all resources of that type
  1399. status = FmpFixupPossibleNodesForResources(TRUE);
  1400. if (status != ERROR_SUCCESS) {
  1401. CsInconsistencyHalt(status);
  1402. return(status);
  1403. }
  1404. if (NmLocalNodeVersionChanged)
  1405. {
  1406. //
  1407. // For each group, allow all resources to do any fixups
  1408. // they might need to do to the cluster registry to
  1409. // run in a mixed mode cluster.
  1410. //
  1411. OmEnumObjects( ObjectTypeGroup,
  1412. FmpEnumFixupResources,
  1413. &ClusterVersionInfo,
  1414. NULL );
  1415. }
  1416. //
  1417. // The FM is now in sync with everybody else.
  1418. //
  1419. FmpFMOnline = TRUE;
  1420. if ( FmpMajorEvent ) {
  1421. return(ERROR_NOT_READY);
  1422. }
  1423. // RAID 513705. Need to send force quorum control to the quorum resource at this point.
  1424. if ( CsForceQuorum ) {
  1425. ASSERT( gpQuoResource ); // Should I assert here, or add "gpQuoResource != NULL" to the
  1426. // if expression?
  1427. status = FmpSendForceQuorumControlToResource( gpQuoResource );
  1428. if ( status != ERROR_SUCCESS ) {
  1429. // The routine does its own logging. Just bail.
  1430. return status;
  1431. }
  1432. }
  1433. status = FmpFixupResourceTypesPhase2(TRUE, NmLocalNodeVersionChanged,
  1434. pClusterVersionInfo);
  1435. if (status != ERROR_SUCCESS) {
  1436. CsInconsistencyHalt(status);
  1437. return(status);
  1438. }
  1439. ClRtlLogPrint(LOG_NOISE,"[FM] FmJoinPhase2 complete, now online!\n");
  1440. return(ERROR_SUCCESS);
  1441. } // FmJoinPhase2
  1442. VOID
  1443. FmJoinPhase3(
  1444. VOID
  1445. )
  1446. /*++
  1447. Routine Description:
  1448. Handles any group moves and resource/group state change signaling as
  1449. a part of join. This MUST be done only AFTER the extended node state
  1450. is UP.
  1451. Arguments:
  1452. None.
  1453. Return Value:
  1454. None.
  1455. --*/
  1456. {
  1457. BOOL formCluster = FALSE;
  1458. DWORD deferred = FALSE;
  1459. ClRtlLogPrint(LOG_NOISE,"[FM] FmJoinPhase3 entry...\n");
  1460. //
  1461. // Chittur Subbaraman (chitturs) - 10/28/99
  1462. //
  1463. //
  1464. // For each group, see if it should be moved to the local system.
  1465. //
  1466. OmEnumObjects( ObjectTypeGroup,
  1467. FmpEnumJoinGroupsMove,
  1468. &deferred,
  1469. NULL );
  1470. //
  1471. // Signal a state change for every group and resource!
  1472. //
  1473. OmEnumObjects( ObjectTypeGroup,
  1474. FmpEnumSignalGroups,
  1475. &formCluster,
  1476. NULL );
  1477. ClRtlLogPrint(LOG_NOISE,"[FM] FmJoinPhase3 exit...\n");
  1478. } // FmJoinPhase3
  1479. BOOL
  1480. FmpFindQuorumResource(
  1481. IN OUT PFM_RESOURCE *QuorumResource,
  1482. IN PVOID Context2,
  1483. IN PFM_RESOURCE Resource,
  1484. IN LPCWSTR Name
  1485. )
  1486. /*++
  1487. Routine Description:
  1488. Group enumeration callback for FM findquorumresource.
  1489. Arguments:
  1490. QuorumResource - Returns the found quorum resource, if found.
  1491. Context2 - Not used.
  1492. Resource - Supplies the current resource.
  1493. Name - Supplies the Resource's name.
  1494. Return Value:
  1495. TRUE - to indicate that the enumeration should continue.
  1496. FALSE - to indicate that the enumeration should not continue.
  1497. --*/
  1498. {
  1499. if ( Resource->QuorumResource ) {
  1500. OmReferenceObject( Resource );
  1501. *QuorumResource = Resource;
  1502. return(FALSE);
  1503. }
  1504. return(TRUE);
  1505. } // FmpFindQuorumResource
  1506. BOOL
  1507. FmArbitrateQuorumResource(
  1508. VOID
  1509. )
  1510. /*++
  1511. Routine Description:
  1512. Arguments:
  1513. Return Value:
  1514. TRUE - if the quorum resource was successfully arbitrated and acquired.
  1515. FALSE - it the quorum resource was not successfully arbitrated.
  1516. --*/
  1517. {
  1518. PFM_RESOURCE resource = NULL;
  1519. DWORD status;
  1520. WCHAR localComputerName[MAX_COMPUTERNAME_LENGTH + 1];
  1521. DWORD localComputerNameSize = MAX_COMPUTERNAME_LENGTH + 1;
  1522. //
  1523. // Next try to find the Quorum resource.
  1524. //
  1525. FmFindQuorumResource(&resource);
  1526. if ( resource == NULL ) {
  1527. SetLastError(ERROR_RESOURCE_NOT_FOUND);
  1528. return(FALSE);
  1529. }
  1530. //
  1531. // Now arbitrate for the resource.
  1532. //
  1533. status = FmpRmArbitrateResource( resource );
  1534. if ( status == ERROR_SUCCESS ) {
  1535. ClRtlLogPrint(LOG_NOISE,
  1536. "[FM] Successfully arbitrated quorum resource %1!ws!.\n",
  1537. OmObjectId(resource));
  1538. ClRtlLogPrint(LOG_NOISE,
  1539. "[FM] FMArbitrateQuoRes: Current State %1!u! State=%2!u! Owner %3!u!\n",
  1540. resource->PersistentState,
  1541. resource->State,
  1542. NmGetNodeId((resource->Group)->OwnerNode));
  1543. ClRtlLogPrint(LOG_NOISE,
  1544. "[FM] FMArbitrateQuoRes: Group state :Current State %1!u! State=%2!u! Owner %3!u!\n",
  1545. resource->Group->PersistentState,
  1546. resource->Group->State,
  1547. NmGetNodeId((resource->Group)->OwnerNode));
  1548. //
  1549. // The quorum resource will be brought online by REGROUP.
  1550. //
  1551. // RNG: what happens if we can't online the quorum resource?
  1552. // A: The node will halt.
  1553. //SS: dereference the object referenced by fmfindquorumresource
  1554. OmDereferenceObject(resource);
  1555. return(TRUE);
  1556. } else {
  1557. ClRtlLogPrint(LOG_CRITICAL,
  1558. "[FM] Failed to arbitrate quorum resource %1!ws!, error %2!u!.\n",
  1559. OmObjectId(resource),
  1560. status);
  1561. //SS: dereference the object referenced by fmfindquorumresource
  1562. OmDereferenceObject(resource);
  1563. return(FALSE);
  1564. }
  1565. } // FmArbitrateQuorumResource
  1566. BOOL
  1567. FmpEnumHoldIO(
  1568. IN PVOID Context1,
  1569. IN PVOID Context2,
  1570. IN PFM_RESTYPE ResType,
  1571. IN LPCWSTR Name
  1572. )
  1573. /*++
  1574. Routine Description:
  1575. Send a HOLD_IO control code to all resource types of class STORAGE.
  1576. Arguments:
  1577. Context1 - Not used.
  1578. Context2 - Not used.
  1579. ResType - Supplies the Resource Type.
  1580. Name - Supplies the Resource Type's name.
  1581. Return Value:
  1582. TRUE - to indicate that the enumeration should continue.
  1583. FALSE - to indicate that the enumeration should not continue.
  1584. --*/
  1585. {
  1586. DWORD dwStatus;
  1587. DWORD bytesReturned;
  1588. DWORD bytesRequired;
  1589. if ( ResType->Class == CLUS_RESCLASS_STORAGE ) {
  1590. ClRtlLogPrint(LOG_NOISE,
  1591. "[FM] Hold IO for storage resource type: %1!ws!\n",
  1592. Name );
  1593. // Hold IO for this resource type
  1594. dwStatus = FmpRmResourceTypeControl(
  1595. Name,
  1596. CLUSCTL_RESOURCE_TYPE_HOLD_IO,
  1597. NULL,
  1598. 0,
  1599. NULL,
  1600. 0,
  1601. &bytesReturned,
  1602. &bytesRequired );
  1603. ClRtlLogPrint(LOG_NOISE,
  1604. "[FM] Resource DLL Hold IO returned status %1!u!\n",
  1605. dwStatus );
  1606. }
  1607. return(TRUE);
  1608. } // FmpEnumHoldIO
  1609. VOID
  1610. FmHoldIO(
  1611. VOID
  1612. )
  1613. /*++
  1614. Routine Description:
  1615. This routine holds all I/O for all storage class resource types.
  1616. It does this by calling the resource dll with a
  1617. CLUSCTL_RESOURCE_TYPE_HOLD_IO resource type control code.
  1618. Inputs:
  1619. None
  1620. Outputs:
  1621. None
  1622. --*/
  1623. {
  1624. OmEnumObjects( ObjectTypeResType,
  1625. FmpEnumHoldIO,
  1626. NULL,
  1627. NULL );
  1628. return;
  1629. } // FmHoldIO
  1630. BOOL
  1631. FmpEnumResumeIO(
  1632. IN PVOID Context1,
  1633. IN PVOID Context2,
  1634. IN PFM_RESTYPE ResType,
  1635. IN LPCWSTR Name
  1636. )
  1637. /*++
  1638. Routine Description:
  1639. Send a RESUME_IO control code to all resource types of class STORAGE.
  1640. Arguments:
  1641. Context1 - Not used.
  1642. Context2 - Not used.
  1643. ResType - Supplies the Resource Type.
  1644. Name - Supplies the Resource Type's name.
  1645. Return Value:
  1646. TRUE - to indicate that the enumeration should continue.
  1647. FALSE - to indicate that the enumeration should not continue.
  1648. --*/
  1649. {
  1650. DWORD dwStatus;
  1651. DWORD bytesReturned;
  1652. DWORD bytesRequired;
  1653. if ( ResType->Class == CLUS_RESCLASS_STORAGE ) {
  1654. ClRtlLogPrint(LOG_NOISE,
  1655. "[FM] Resume IO for storage Resource Type %1!ws!\n",
  1656. Name );
  1657. // Resume IO for this resource type
  1658. dwStatus = FmpRmResourceTypeControl(
  1659. Name,
  1660. CLUSCTL_RESOURCE_TYPE_RESUME_IO,
  1661. NULL,
  1662. 0,
  1663. NULL,
  1664. 0,
  1665. &bytesReturned,
  1666. &bytesRequired );
  1667. ClRtlLogPrint(LOG_NOISE,
  1668. "[FM] Resource DLL Resume IO returned status %1!u!\n",
  1669. dwStatus );
  1670. }
  1671. return(TRUE);
  1672. } // FmpEnumResumeIO
  1673. VOID
  1674. FmResumeIO(
  1675. VOID
  1676. )
  1677. /*++
  1678. Routine Description:
  1679. This routine resumes all I/O for all storage class resource types.
  1680. It does this by calling the resource dll with a
  1681. CLUSCTL_RESOURCE_TYPE_RESUME_IO resource type control code.
  1682. Inputs:
  1683. None
  1684. Outputs:
  1685. None
  1686. --*/
  1687. {
  1688. OmEnumObjects( ObjectTypeResType,
  1689. FmpEnumResumeIO,
  1690. NULL,
  1691. NULL );
  1692. return;
  1693. } // FmResumeIO
  1694. BOOL
  1695. FmpEnumNodes(
  1696. OUT DWORD *pStatus,
  1697. IN PVOID Context2,
  1698. IN PNM_NODE Node,
  1699. IN LPCWSTR Name
  1700. )
  1701. /*++
  1702. Routine Description:
  1703. Node enumeration callback for FM join. Queries the state
  1704. of owned groups and resources for each online node.
  1705. Arguments:
  1706. pStatus - Returns any error that may occur.
  1707. Context2 - Not used
  1708. Node - Supplies the node.
  1709. Name - Supplies the node's name.
  1710. Return Value:
  1711. TRUE - to indicate that the enumeration should continue.
  1712. FALSE - to indicate that the enumeration should not continue.
  1713. --*/
  1714. {
  1715. DWORD Status;
  1716. DWORD NodeId;
  1717. PGROUP_ENUM NodeGroups = NULL;
  1718. PRESOURCE_ENUM NodeResources = NULL;
  1719. DWORD i;
  1720. PFM_GROUP Group;
  1721. PFM_RESOURCE Resource;
  1722. if (Node == NmLocalNode) {
  1723. CL_ASSERT(NmGetNodeState(Node) != ClusterNodeUp);
  1724. return(TRUE);
  1725. }
  1726. //
  1727. // Enumerate all other node's group states. This includes all nodes
  1728. // that are up, as well as nodes that are paused.
  1729. //
  1730. if ((NmGetNodeState(Node) == ClusterNodeUp) ||
  1731. (NmGetNodeState(Node) == ClusterNodePaused)){
  1732. NodeId = NmGetNodeId(Node);
  1733. CL_ASSERT(Session[NodeId] != NULL);
  1734. Status = FmsQueryOwnedGroups(Session[NodeId],
  1735. &NodeGroups,
  1736. &NodeResources);
  1737. if (Status != ERROR_SUCCESS) {
  1738. ClRtlLogPrint(LOG_UNUSUAL,
  1739. "[FM] FmsQueryOwnedGroups to node %1!ws! failed %2!d!\n",
  1740. OmObjectId(Node),
  1741. Status);
  1742. *pStatus = Status;
  1743. return(FALSE);
  1744. }
  1745. //
  1746. // Enumerate the groups and set their owner and state.
  1747. //
  1748. for (i=0; i < NodeGroups->EntryCount; i++) {
  1749. Group = OmReferenceObjectById(ObjectTypeGroup,
  1750. NodeGroups->Entry[i].Id);
  1751. if (Group == NULL) {
  1752. ClRtlLogPrint(LOG_UNUSUAL,
  1753. "[FM] FmpEnumNodes: group %1!ws! not found\n",
  1754. NodeGroups->Entry[i].Id);
  1755. } else {
  1756. if ( FmpInPreferredList( Group, Node, FALSE, NULL ) ) {
  1757. ClRtlLogPrint(LOG_NOISE,
  1758. "[FM] Setting group %1!ws! owner to node %2!ws!, state %3!d!\n",
  1759. OmObjectId(Group),
  1760. OmObjectId(Node),
  1761. NodeGroups->Entry[i].State);
  1762. } else {
  1763. ClRtlLogPrint(LOG_NOISE,
  1764. "[FM] Init, Node %1!ws! is not in group %2!ws!.\n",
  1765. OmObjectId(Node),
  1766. OmObjectId(Group));
  1767. }
  1768. OmReferenceObject( Node );
  1769. Group->OwnerNode = Node;
  1770. Group->State = NodeGroups->Entry[i].State;
  1771. Group->StateSequence = NodeGroups->Entry[i].StateSequence;
  1772. OmDereferenceObject(Group);
  1773. }
  1774. MIDL_user_free(NodeGroups->Entry[i].Id);
  1775. }
  1776. MIDL_user_free(NodeGroups);
  1777. //
  1778. // Enumerate the resources and set their current state.
  1779. //
  1780. for (i=0; i < NodeResources->EntryCount; i++) {
  1781. Resource = OmReferenceObjectById(ObjectTypeResource,
  1782. NodeResources->Entry[i].Id);
  1783. if (Resource == NULL) {
  1784. ClRtlLogPrint(LOG_UNUSUAL,
  1785. "[FM] FmpEnumNodes: resource %1!ws! not found\n",
  1786. NodeResources->Entry[i].Id);
  1787. } else {
  1788. ClRtlLogPrint(LOG_NOISE,
  1789. "[FM] Setting resource %1!ws! state to %2!d!\n",
  1790. OmObjectId(Resource),
  1791. NodeResources->Entry[i].State);
  1792. Resource->State = NodeResources->Entry[i].State;
  1793. Resource->StateSequence = NodeResources->Entry[i].StateSequence;
  1794. OmDereferenceObject(Resource);
  1795. }
  1796. MIDL_user_free(NodeResources->Entry[i].Id);
  1797. }
  1798. MIDL_user_free(NodeResources);
  1799. }
  1800. return(TRUE);
  1801. } // FmpEnumNodes
  1802. VOID
  1803. WINAPI
  1804. FmShutdown(
  1805. VOID
  1806. )
  1807. /*++
  1808. Routine Description:
  1809. Shuts down the Failover Manager
  1810. Arguments:
  1811. None
  1812. Return Value:
  1813. None.
  1814. --*/
  1815. {
  1816. DWORD i;
  1817. if ( !FmpInitialized ) {
  1818. return;
  1819. }
  1820. FmpInitialized = FALSE;
  1821. ClRtlLogPrint(LOG_UNUSUAL,
  1822. "[FM] Shutdown: Failover Manager requested to shutdown.\n");
  1823. //
  1824. // For now, we really can't delete these critical sections. There is a
  1825. // race condition where the FM is shutting down and someone is walking
  1826. // the lists. Keep this critical sections around... just in case.
  1827. //
  1828. //DeleteCriticalSection( &FmpResourceLock );
  1829. //DeleteCriticalSection( &FmpGroupLock );
  1830. //DeleteCriticalSection( &FmpMonitorLock );
  1831. if ( FmpDefaultMonitor != NULL ) {
  1832. FmpShutdownMonitor(FmpDefaultMonitor);
  1833. FmpDefaultMonitor = NULL;
  1834. }
  1835. CloseHandle( FmpShutdownEvent );
  1836. #if 0 // RNG - don't run the risk of other threads using these handles
  1837. for ( i = ClusterMinNodeId; i <= NmMaxNodeId; i++ ) {
  1838. if ( FmpRpcBindings[i] != NULL ) {
  1839. ClMsgDeleteRpcBinding( FmpRpcBindings[i] );
  1840. FmpRpcBindings[i] = NULL;
  1841. }
  1842. if ( FmpRpcQuorumBindings[i] != NULL ) {
  1843. ClMsgDeleteRpcBinding( FmpRpcQuorumBindings[i] );
  1844. FmpRpcQuorumBindings[i] = NULL;
  1845. }
  1846. }
  1847. #endif
  1848. ClRtlDeleteQueue( &FmpWorkQueue );
  1849. return;
  1850. } // FmShutdown
  1851. VOID
  1852. WINAPI
  1853. FmShutdownGroups(
  1854. VOID
  1855. )
  1856. /*++
  1857. Routine Description:
  1858. Moves or takes offline all groups owned by this node.
  1859. Arguments:
  1860. None
  1861. Return Value:
  1862. None.
  1863. --*/
  1864. {
  1865. ClRtlLogPrint(LOG_UNUSUAL,
  1866. "[FM] Shutdown: Failover Manager requested to shutdown groups.\n");
  1867. //if we didnt initialize, we dont have to do anything
  1868. if (!FmpInitialized)
  1869. return;
  1870. //
  1871. // Use the Group Lock to synchronize the shutdown
  1872. //
  1873. FmpAcquireGroupLock();
  1874. //if shutdown is already in progress, return
  1875. if ( FmpShutdown) {
  1876. FmpReleaseGroupLock();
  1877. return;
  1878. }
  1879. FmpShutdown = TRUE;
  1880. FmpFMOnline = FALSE;
  1881. FmpReleaseGroupLock();
  1882. //
  1883. // Now cleanup all Groups/Resources.
  1884. //
  1885. FmpCleanupGroups(TRUE);
  1886. return;
  1887. } // FmShutdownGroups
  1888. /****
  1889. @func DWORD | FmBringQuorumOnline| This routine finds the quorum resource and
  1890. brings it online.
  1891. @comm This is called by the FmFormClusterPhase 1.
  1892. @xref
  1893. ****/
  1894. DWORD FmBringQuorumOnline()
  1895. {
  1896. PFM_RESOURCE pQuoResource;
  1897. DWORD dwError=ERROR_SUCCESS;
  1898. //
  1899. // Synchronize with shutdown.
  1900. //
  1901. FmpAcquireGroupLock();
  1902. if ( FmpShutdown ) {
  1903. FmpReleaseGroupLock();
  1904. return(ERROR_SUCCESS);
  1905. }
  1906. if ((dwError = FmFindQuorumResource(&pQuoResource)) != ERROR_SUCCESS)
  1907. {
  1908. ClRtlLogPrint(LOG_UNUSUAL,
  1909. "[Fm] FmpBringQuorumOnline : failed to find resource 0x%1!08lx!\n",
  1910. dwError);
  1911. goto FnExit;
  1912. }
  1913. //mark yourself as owner
  1914. if ( pQuoResource->Group->OwnerNode != NULL )
  1915. {
  1916. OmDereferenceObject( pQuoResource->Group->OwnerNode );
  1917. }
  1918. OmReferenceObject( NmLocalNode );
  1919. pQuoResource->Group->OwnerNode = NmLocalNode;
  1920. //prepare the group for onlining it
  1921. FmpPrepareGroupForOnline(pQuoResource->Group);
  1922. dwError = FmpOnlineResource(pQuoResource, TRUE);
  1923. //SS:decrement the ref count on the quorum resource object
  1924. //provided by fmfindquorumresource
  1925. OmDereferenceObject(pQuoResource);
  1926. FnExit:
  1927. FmpReleaseGroupLock();
  1928. return(dwError);
  1929. }
  1930. /****
  1931. @func DWORD | FmpGetQuorumDiskSignature | Get the signature of
  1932. the quorum disk from the cluster hive.
  1933. @parm IN LPWSTR | lpQuorumId | Identifier of the quorum resource.
  1934. @parm OUT LPDWORD | lpdwSignature | Quorum disk signature.
  1935. @rdesc Returns a Win32 error code on failure. ERROR_SUCCESS on success.
  1936. @comm This function attempts to open the Resources\lpQuorumId\Parameters
  1937. key under the cluster hive and read the quorum disk signature.
  1938. @xref <f FmGetQuorumResource>
  1939. ****/
  1940. DWORD
  1941. FmpGetQuorumDiskSignature(
  1942. IN LPCWSTR lpQuorumId,
  1943. OUT LPDWORD lpdwSignature
  1944. )
  1945. {
  1946. HDMKEY hQuorumResKey = NULL;
  1947. HDMKEY hQuorumResParametersKey = NULL;
  1948. DWORD dwStatus = ERROR_SUCCESS;
  1949. //
  1950. // Chittur Subbaraman (chitturs) - 10/30/98
  1951. //
  1952. hQuorumResKey = DmOpenKey( DmResourcesKey,
  1953. lpQuorumId,
  1954. KEY_READ );
  1955. if ( hQuorumResKey != NULL )
  1956. {
  1957. //
  1958. // Open up the Parameters key
  1959. //
  1960. hQuorumResParametersKey = DmOpenKey( hQuorumResKey,
  1961. CLUSREG_KEYNAME_PARAMETERS,
  1962. KEY_READ );
  1963. DmCloseKey( hQuorumResKey );
  1964. if ( hQuorumResParametersKey != NULL )
  1965. {
  1966. //
  1967. // Read the disk signature value
  1968. //
  1969. dwStatus = DmQueryDword( hQuorumResParametersKey,
  1970. CLUSREG_NAME_PHYSDISK_SIGNATURE,
  1971. lpdwSignature,
  1972. NULL );
  1973. DmCloseKey( hQuorumResParametersKey );
  1974. } else
  1975. {
  1976. dwStatus = GetLastError();
  1977. }
  1978. } else
  1979. {
  1980. dwStatus = GetLastError();
  1981. }
  1982. //
  1983. // If you failed, then reset the signature to 0 so that the
  1984. // caller won't take any actions based on an invalid signature.
  1985. //
  1986. if ( dwStatus != ERROR_SUCCESS )
  1987. {
  1988. *lpdwSignature = 0;
  1989. }
  1990. return( dwStatus );
  1991. }
  1992. DWORD FmpGetJoinApproval()
  1993. {
  1994. DWORD dwStatus;
  1995. LPCWSTR pszNodeId;
  1996. DWORD dwNodeLen;
  1997. pszNodeId = OmObjectId(NmLocalNode);
  1998. dwNodeLen = (lstrlenW(pszNodeId)+1)*sizeof(WCHAR);
  1999. dwStatus = GumSendUpdateEx(
  2000. GumUpdateFailoverManager,
  2001. FmUpdateApproveJoin,
  2002. 1,
  2003. dwNodeLen,
  2004. pszNodeId);
  2005. return(dwStatus);
  2006. }
  2007. /****
  2008. @func DWORD | FmpBuildForceQuorumInfo | Build the force quorum info that
  2009. will be passed to the resource DLL via a control code. This
  2010. involves enumerating nodes and checking that the nodes that make up
  2011. the list passed on the command line are all valid cluster nodes.
  2012. @parm IN LPCWSTR | pszNodesIn | Comma separated list of node names. If
  2013. this is NULL then the routine just fills the quorum info structure
  2014. with 0 and a NULL node list.
  2015. @parm OUT PCLUS_FORCE_QUORUM_INFO | pForceQuorumInfo | Structure that gets
  2016. filled in with info
  2017. @rdesc Returns a Win32 error code on failure. ERROR_SUCCESS on success.
  2018. @comm Assumes NmInitialize was called prior to calling this routine.
  2019. @xref <f FmpBuildForceQuorumInfo>
  2020. ****/
  2021. static
  2022. DWORD
  2023. FmpBuildForceQuorumInfo(
  2024. IN LPCWSTR pszNodesIn,
  2025. OUT PCLUS_FORCE_QUORUM_INFO* ppForceQuorumInfo
  2026. )
  2027. {
  2028. WCHAR *pszOut = NULL;
  2029. WCHAR *pszComma = NULL;
  2030. DWORD status = ERROR_SUCCESS;
  2031. PNM_NODE_ENUM2 pNodeEnum = NULL;
  2032. int iCurrLen = 0, iOffset = 0;
  2033. DWORD dwNodeIndex;
  2034. DWORD dwSize;
  2035. PCLUS_FORCE_QUORUM_INFO pForceQuorumInfo = NULL;
  2036. // Need to allocate a structure that can hold the nodes list.
  2037. //
  2038. dwSize = sizeof( CLUS_FORCE_QUORUM_INFO ) + sizeof( WCHAR ) * (wcslen( pszNodesIn ) + 1);
  2039. pForceQuorumInfo = LocalAlloc( LMEM_FIXED, dwSize );
  2040. if ( pForceQuorumInfo == NULL ) {
  2041. status = ERROR_NOT_ENOUGH_MEMORY;
  2042. goto ErrorExit;
  2043. }
  2044. ZeroMemory( pForceQuorumInfo, dwSize );
  2045. pForceQuorumInfo->dwSize = dwSize;
  2046. pForceQuorumInfo->dwNodeBitMask = 0;
  2047. pForceQuorumInfo->dwMaxNumberofNodes = 0;
  2048. if ( pszNodesIn == NULL ) {
  2049. pForceQuorumInfo->multiszNodeList[0] = L'\0';
  2050. goto ret;
  2051. }
  2052. ClRtlLogPrint( LOG_NOISE, "[Fm] FmpBuildForceQuorumInfo: pszNodesIn is %1!ws!\n",
  2053. pszNodesIn );
  2054. // Now get the enumeration of all cluster nodes so we can check we have
  2055. // valid nodes in the list.
  2056. //
  2057. status = NmpEnumNodeDefinitions( &pNodeEnum );
  2058. if ( status != ERROR_SUCCESS )
  2059. goto ErrorExit;
  2060. // Go through all the nodes we have and ensure that they are cluster nodes.
  2061. // Get the corresponding ID and incorporate in the bitmask
  2062. //
  2063. do {
  2064. pszComma = wcschr( pszNodesIn, (int) L',');
  2065. if ( pszComma == NULL )
  2066. iCurrLen = wcslen( pszNodesIn );
  2067. else
  2068. iCurrLen = (int) (pszComma - pszNodesIn);
  2069. // At this point pszNodesIn is the start of a node name, iCurrLen chars long
  2070. // or iCurrLen is 0 in which case we have ,, in the input stream.
  2071. //
  2072. if (iCurrLen > 0) {
  2073. // Work out if this node is part of the cluster and if so get its
  2074. // ID and setup the bitmask.
  2075. //
  2076. for ( dwNodeIndex = 0; dwNodeIndex < pNodeEnum->NodeCount; dwNodeIndex++ ) {
  2077. int iNodeNameLen = wcslen( pNodeEnum->NodeList[ dwNodeIndex ].NodeName );
  2078. ClRtlLogPrint( LOG_NOISE, "[Fm] FmpBuildForceQuorumInfo: trying %1!ws!\n",
  2079. pNodeEnum->NodeList[ dwNodeIndex ].NodeName );
  2080. if ( ClRtlStrNICmp( pNodeEnum->NodeList[ dwNodeIndex ].NodeName,
  2081. pszNodesIn,
  2082. max(iCurrLen, iNodeNameLen) ) == 0 ) {
  2083. // Using wcstoul here to get the nodeId rather than using
  2084. PWSTR ignore;
  2085. DWORD nodeId = wcstoul( pNodeEnum->NodeList[ dwNodeIndex ].NodeId, &ignore, 10 );
  2086. ClRtlLogPrint( LOG_NOISE, "[Fm] FmpBuildForceQuorumInfo: got match %1!ws!\n",
  2087. pNodeEnum->NodeList[ dwNodeIndex ].NodeName );
  2088. // Set the mask and max nodes and break - ignore duplicates.
  2089. //
  2090. if ( !( pForceQuorumInfo->dwNodeBitMask & ( 1 << nodeId )) ) {
  2091. pForceQuorumInfo->dwMaxNumberofNodes += 1;
  2092. pForceQuorumInfo->dwNodeBitMask |= ( 1 << nodeId );
  2093. wcscpy( &pForceQuorumInfo->multiszNodeList[iOffset], pNodeEnum->NodeList[ dwNodeIndex ].NodeName );
  2094. iOffset += wcslen( pNodeEnum->NodeList[ dwNodeIndex ].NodeName ) + 1;
  2095. }
  2096. break;
  2097. }
  2098. }
  2099. if ( dwNodeIndex == pNodeEnum->NodeCount ) {
  2100. ClRtlLogPrint( LOG_UNUSUAL, "[Fm] FmpBuildForceQuorumInfo: no match for %1!ws!\n", pszNodesIn );
  2101. status = ERROR_INVALID_PARAMETER;
  2102. goto ErrorExit;
  2103. }
  2104. } else if ( pszComma != NULL ) {
  2105. ClRtlLogPrint( LOG_UNUSUAL,
  2106. "[Fm] FmpBuildForceQuorumInfo: iCurrLen was 0 so ,, was in node list: %1!ws!\n",
  2107. CsForceQuorumNodes );
  2108. status = ERROR_INVALID_PARAMETER;
  2109. goto ErrorExit;
  2110. }
  2111. pszNodesIn = pszComma + 1;
  2112. } while ( pszComma != NULL);
  2113. pForceQuorumInfo->multiszNodeList[ iOffset ] = L'\0';
  2114. goto ret;
  2115. ErrorExit:
  2116. if ( pForceQuorumInfo != NULL ) {
  2117. LocalFree( pForceQuorumInfo );
  2118. pForceQuorumInfo = NULL;
  2119. }
  2120. ret:
  2121. if ( pNodeEnum != NULL ) {
  2122. ClNetFreeNodeEnum( pNodeEnum );
  2123. }
  2124. if ( status == ERROR_SUCCESS ) {
  2125. *ppForceQuorumInfo = pForceQuorumInfo;
  2126. ClRtlLogPrint( LOG_NOISE,
  2127. "[Fm] FmpBuildForceQuorumInfo: success; mask is 0x%1!08x!\n",
  2128. pForceQuorumInfo->dwNodeBitMask );
  2129. }
  2130. return status;
  2131. }
  2132. static
  2133. void
  2134. FmpDeleteForceQuorumInfo(
  2135. IN OUT PCLUS_FORCE_QUORUM_INFO* ppForceQuorumInfo
  2136. )
  2137. {
  2138. (void) LocalFree( *ppForceQuorumInfo );
  2139. *ppForceQuorumInfo = NULL;
  2140. }