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

2677 lines
74 KiB

  1. /*++
  2. Copyright (c) 1996-1997 Microsoft Corporation
  3. Module Name:
  4. fmreg.c
  5. Abstract:
  6. Object Manager registry query routines for the Failover Manager
  7. component of the NT Cluster Service.
  8. Author:
  9. Rod Gamache (rodga) 14-Mar-1996
  10. Revision History:
  11. --*/
  12. #include "fmp.h"
  13. #include <stdlib.h>
  14. #include <search.h>
  15. #define LOG_MODULE FMREG
  16. //
  17. // Global data initialized in this module
  18. //
  19. ULONG FmpUnknownCount = 0;
  20. //
  21. // Local functions
  22. //
  23. VOID
  24. FmpGroupChangeCallback(
  25. IN DWORD_PTR Context1,
  26. IN DWORD_PTR Context2,
  27. IN DWORD CompletionFilter,
  28. IN LPCWSTR RelativeName
  29. );
  30. VOID
  31. FmpResourceChangeCallback(
  32. IN DWORD_PTR Context1,
  33. IN DWORD_PTR Context2,
  34. IN DWORD CompletionFilter,
  35. IN LPCWSTR RelativeName
  36. );
  37. /////////////////////////////////////////////////////////////////////////////
  38. //
  39. // Configuration Database Access Routines
  40. //
  41. /////////////////////////////////////////////////////////////////////////////
  42. DWORD
  43. FmpRegEnumerateKey(
  44. IN HDMKEY ListKey,
  45. IN DWORD Index,
  46. OUT LPWSTR *Name,
  47. IN OUT LPDWORD NameMaxSize
  48. )
  49. /*++
  50. Routine Description:
  51. Arguments:
  52. Returns:
  53. --*/
  54. {
  55. DWORD status;
  56. FILETIME fileTime;
  57. status = DmEnumKey( ListKey,
  58. Index,
  59. *Name,
  60. NameMaxSize,
  61. NULL );
  62. if ( status == ERROR_SUCCESS ) {
  63. return(ERROR_SUCCESS);
  64. }
  65. if ( status == ERROR_MORE_DATA ) {
  66. PWCHAR nameString = NULL;
  67. DWORD maxSubkeyNameSize = 0;
  68. DWORD temp = 0;
  69. //
  70. // The name string isn't big enough. Reallocate it.
  71. //
  72. //
  73. // Find out the length of the longest subkey name.
  74. //
  75. status = DmQueryInfoKey( ListKey,
  76. &temp,
  77. &maxSubkeyNameSize,
  78. &temp,
  79. &temp,
  80. &temp,
  81. NULL,
  82. &fileTime );
  83. if ( (status != ERROR_SUCCESS) &&
  84. (status != ERROR_MORE_DATA) ) {
  85. ClRtlLogPrint(LOG_NOISE,"[FM] DmQueryInfoKey returned status %1!u!\n",
  86. status);
  87. return(status);
  88. }
  89. CL_ASSERT(maxSubkeyNameSize != 0);
  90. //
  91. // The returned subkey name size does not include the terminating null.
  92. // It is also an ANSI string count.
  93. //
  94. maxSubkeyNameSize *= sizeof(WCHAR);
  95. maxSubkeyNameSize += sizeof(UNICODE_NULL);
  96. nameString = LocalAlloc( LMEM_FIXED,
  97. maxSubkeyNameSize );
  98. if ( nameString == NULL ) {
  99. ClRtlLogPrint(LOG_NOISE,
  100. "[FM] Unable to allocate key name buffer of size %1!u!\n",
  101. maxSubkeyNameSize
  102. );
  103. return(ERROR_NOT_ENOUGH_MEMORY);
  104. }
  105. LocalFree(*Name);
  106. *Name = nameString;
  107. *NameMaxSize = maxSubkeyNameSize;
  108. status = DmEnumKey( ListKey,
  109. Index,
  110. *Name,
  111. NameMaxSize,
  112. NULL );
  113. CL_ASSERT(status != ERROR_MORE_DATA);
  114. CL_ASSERT(status != ERROR_NO_MORE_ITEMS);
  115. }
  116. return(status);
  117. } // FmpRegEnumerateKey
  118. VOID
  119. FmpPruneGroupOwners(
  120. IN PFM_GROUP Group
  121. )
  122. /*++
  123. Routine Description:
  124. Prunes the entire preferred group list based on the possible
  125. nodes of each resource in the group.
  126. Arguments:
  127. Group - Supplies the group object to be pruned
  128. Return Value:
  129. None.
  130. --*/
  131. {
  132. PLIST_ENTRY ListEntry;
  133. PFM_RESOURCE Resource;
  134. ListEntry = Group->Contains.Flink;
  135. while (ListEntry != &Group->Contains) {
  136. Resource = CONTAINING_RECORD(ListEntry,
  137. FM_RESOURCE,
  138. ContainsLinkage);
  139. FmpPrunePreferredList(Resource);
  140. ListEntry = ListEntry->Flink;
  141. }
  142. return;
  143. }
  144. VOID
  145. FmpPrunePreferredList(
  146. IN PFM_RESOURCE Resource
  147. )
  148. /*++
  149. Routine Description:
  150. Prune out nodes from the preferred owners list, if the resource cannot
  151. run on that node.
  152. Arguments:
  153. Resource - Pointer to the resource object with a possible owners list.
  154. Return Value:
  155. None.
  156. --*/
  157. {
  158. PFM_GROUP group;
  159. PLIST_ENTRY listEntry;
  160. PLIST_ENTRY entry;
  161. PPREFERRED_ENTRY preferredEntry;
  162. PPOSSIBLE_ENTRY possibleEntry;
  163. DWORD orderedEntry = 0;
  164. group = Resource->Group;
  165. //
  166. // For each entry in the Preferred list, it must exist in the possible
  167. // list.
  168. //
  169. for ( listEntry = group->PreferredOwners.Flink;
  170. listEntry != &(group->PreferredOwners);
  171. ) {
  172. preferredEntry = CONTAINING_RECORD( listEntry,
  173. PREFERRED_ENTRY,
  174. PreferredLinkage );
  175. //
  176. // Scan the Possible owners list in the resource to make sure that
  177. // the group can run on all of the preferred owners.
  178. //
  179. for ( entry = Resource->PossibleOwners.Flink;
  180. entry != &(Resource->PossibleOwners);
  181. entry = entry->Flink ) {
  182. possibleEntry = CONTAINING_RECORD( entry,
  183. POSSIBLE_ENTRY,
  184. PossibleLinkage );
  185. if ( preferredEntry->PreferredNode == possibleEntry->PossibleNode ) {
  186. break;
  187. }
  188. }
  189. listEntry = listEntry->Flink;
  190. //
  191. // If we got to the end of the possible owners list and didn't find
  192. // an entry, then remove the current preferred entry.
  193. //
  194. if ( entry == &(Resource->PossibleOwners) ) {
  195. ClRtlLogPrint( LOG_NOISE,
  196. "[FM] Removing preferred node %1!ws! because of resource %2!ws!\n",
  197. OmObjectId(preferredEntry->PreferredNode),
  198. OmObjectId(Resource));
  199. //
  200. // If this was an ordered entry, then decrement count.
  201. //
  202. if ( orderedEntry < group->OrderedOwners ) {
  203. --group->OrderedOwners;
  204. }
  205. RemoveEntryList( &preferredEntry->PreferredLinkage );
  206. OmDereferenceObject(preferredEntry->PreferredNode);
  207. LocalFree(preferredEntry);
  208. if ( IsListEmpty( &group->PreferredOwners ) ) {
  209. ClRtlLogPrint( LOG_ERROR,
  210. "[FM] Preferred owners list is now empty! No place to run group %1!ws!\n",
  211. OmObjectId(group));
  212. }
  213. } else {
  214. orderedEntry++;
  215. }
  216. }
  217. } // FmpPrunePreferredList
  218. BOOL
  219. FmpAddNodeToPrefList(
  220. IN PNM_NODE Node,
  221. IN PFM_GROUP Group
  222. )
  223. /*++
  224. Routine Description:
  225. Node enumeration callback for including all remaining nodes
  226. in a group's preferred owners list.
  227. Arguments:
  228. Group - a pointer to the group object to add this node as a preferred owner.
  229. Context2 - Not used
  230. Node - Supplies the node.
  231. Name - Supplies the node's name.
  232. Return Value:
  233. TRUE - to indicate that the enumeration should continue.
  234. FALSE - to indicate that the enumeration should not continue.
  235. --*/
  236. {
  237. //if it is already in the list FmpSetPrefferedEntry returns ERROR_SUCCESS
  238. if ( FmpSetPreferredEntry( Group, Node ) != ERROR_SUCCESS ) {
  239. return(FALSE);
  240. }
  241. return(TRUE);
  242. } // FmpAddNodeToPrefList
  243. BOOL
  244. FmpAddNodeToListCb(
  245. IN OUT PNM_NODE_ENUM2 *ppNmNodeEnum,
  246. IN LPDWORD pdwAllocatedEntries,
  247. IN PNM_NODE pNode,
  248. IN LPCWSTR Id
  249. )
  250. /*++
  251. Routine Description:
  252. Worker callback routine for the enumeration of nodes.
  253. This routine adds the specified node to the list that is being
  254. generated.
  255. Arguments:
  256. ppNmNodeEnum - The node Enumeration list. Can be an output if a new list is
  257. allocated.
  258. EnumData - Supplies the current enumeration data structure.
  259. Group - The Group object being enumerated.
  260. Id - The Id of the node object being enumerated.
  261. Returns:
  262. TRUE - to indicate that the enumeration should continue.
  263. Side Effects:
  264. Makes the quorum group first in the list.
  265. --*/
  266. {
  267. PNM_NODE_ENUM2 pNmNodeEnum;
  268. PNM_NODE_ENUM2 pNewNmNodeEnum;
  269. DWORD dwNewAllocated;
  270. DWORD dwStatus;
  271. pNmNodeEnum = *ppNmNodeEnum;
  272. if ( pNmNodeEnum->NodeCount >= *pdwAllocatedEntries )
  273. {
  274. //
  275. // Time to grow the GROUP_ENUM
  276. //
  277. dwNewAllocated = *pdwAllocatedEntries + ENUM_GROW_SIZE;
  278. pNewNmNodeEnum = LocalAlloc(LMEM_FIXED, NODE_SIZE(dwNewAllocated));
  279. if ( pNewNmNodeEnum == NULL )
  280. {
  281. dwStatus = ERROR_NOT_ENOUGH_MEMORY;
  282. CL_UNEXPECTED_ERROR(dwStatus);
  283. return(FALSE);
  284. }
  285. CopyMemory(pNewNmNodeEnum, pNmNodeEnum, NODE_SIZE(*pdwAllocatedEntries));
  286. *pdwAllocatedEntries = dwNewAllocated;
  287. *ppNmNodeEnum = pNewNmNodeEnum;
  288. LocalFree(pNmNodeEnum);
  289. pNmNodeEnum = pNewNmNodeEnum;
  290. }
  291. //dont copy more that the sixe
  292. lstrcpyW(pNmNodeEnum->NodeList[pNmNodeEnum->NodeCount].NodeId, Id );
  293. ++pNmNodeEnum->NodeCount;
  294. return(TRUE);
  295. } // FmpAddNodeToListCb
  296. int
  297. __cdecl
  298. SortNodesInAscending(
  299. const PVOID Elem1,
  300. const PVOID Elem2
  301. )
  302. {
  303. PNM_NODE_INFO2 El1 = (PNM_NODE_INFO2)Elem1;
  304. PNM_NODE_INFO2 El2 = (PNM_NODE_INFO2)Elem2;
  305. return(lstrcmpiW( El1->NodeId, El2->NodeId ));
  306. }// SortNodesInAsceding
  307. DWORD
  308. FmpEnumNodesById(
  309. IN DWORD dwOptions,
  310. OUT PNM_NODE_ENUM2 *ppNodeEnum
  311. )
  312. /*++
  313. Routine Description:
  314. Enumerates and sorts the list of Groups.
  315. Arguments:
  316. *ppNodeEnum - Returns the requested objects.
  317. dwOptions -
  318. Return Value:
  319. ERROR_SUCCESS if successful.
  320. Win32 error code on error.
  321. --*/
  322. {
  323. DWORD dwStatus;
  324. PNM_NODE_ENUM2 pNmNodeEnum = NULL;
  325. DWORD dwAllocatedEntries;
  326. //
  327. // initialize output params to NULL
  328. //
  329. *ppNodeEnum = NULL;
  330. dwAllocatedEntries = ENUM_GROW_SIZE;
  331. pNmNodeEnum = LocalAlloc( LMEM_FIXED, NODE_SIZE(ENUM_GROW_SIZE) );
  332. if ( pNmNodeEnum == NULL ) {
  333. dwStatus = ERROR_NOT_ENOUGH_MEMORY;
  334. goto error_exit;
  335. }
  336. pNmNodeEnum->NodeCount = 0;
  337. //
  338. // Enumerate all nodes
  339. //
  340. OmEnumObjects( ObjectTypeNode,
  341. FmpAddNodeToListCb,
  342. &pNmNodeEnum,
  343. &dwAllocatedEntries );
  344. CL_ASSERT( pNmNodeEnum->NodeCount != 0 );
  345. //
  346. // Sort the groups by their collating sequence number.
  347. //
  348. qsort( (PVOID)(&pNmNodeEnum->NodeList[0]),
  349. (size_t)pNmNodeEnum->NodeCount,
  350. sizeof(NM_NODE_INFO2),
  351. (int (__cdecl *)(const void*, const void*)) SortNodesInAscending
  352. );
  353. *ppNodeEnum = pNmNodeEnum;
  354. return( ERROR_SUCCESS );
  355. error_exit:
  356. if ( pNmNodeEnum != NULL ) {
  357. LocalFree( pNmNodeEnum );
  358. }
  359. return( dwStatus );
  360. } // FmpEnumNodesById
  361. BOOL
  362. FmpEnumAddAllOwners(
  363. IN PFM_RESOURCE Resource,
  364. IN PVOID Context2,
  365. IN PNM_NODE Node,
  366. IN LPCWSTR Name
  367. )
  368. /*++
  369. Routine Description:
  370. Node enumeration callback for adding all nodes to a resource's
  371. list of possible nodes.
  372. Arguments:
  373. Resource - a pointer to the resource object to add this node as a possible owner.
  374. Context2 - Not used
  375. Node - Supplies the node.
  376. Name - Supplies the node's name.
  377. Return Value:
  378. TRUE - to indicate that the enumeration should continue.
  379. FALSE - to indicate that the enumeration should not continue.
  380. --*/
  381. {
  382. if ( !Resource->PossibleList ) {
  383. FmpAddPossibleEntry(Resource, Node);
  384. }
  385. return(TRUE);
  386. } // FmpEnumAddAllOwners
  387. DWORD
  388. FmpQueryGroupNodes(
  389. IN PFM_GROUP Group,
  390. IN HDMKEY hGroupKey
  391. )
  392. /*++
  393. Routine Description:
  394. Rebuilds and orders the list of preferred nodes associated with
  395. a group.
  396. Arguments:
  397. Group - Supplies the group whose list of preferred nodes should
  398. be rebuilt.
  399. hGroupKey - Supplies a handle to the group's registry key
  400. Return Value:
  401. ERROR_SUCCESS if successful
  402. Win32 error code otherwise
  403. --*/
  404. {
  405. LPWSTR preferredOwnersString = NULL;
  406. DWORD preferredOwnersStringSize = 0;
  407. DWORD preferredOwnersStringMaxSize = 0;
  408. DWORD mszStringIndex;
  409. PPREFERRED_ENTRY preferredEntry;
  410. DWORD status;
  411. PLIST_ENTRY listEntry;
  412. PNM_NODE_ENUM2 pNmNodeEnum = NULL;
  413. PNM_NODE pNmNode;
  414. DWORD i;
  415. //
  416. // First, delete the old list.
  417. //
  418. while ( !IsListEmpty(&Group->PreferredOwners) ) {
  419. listEntry = Group->PreferredOwners.Flink;
  420. preferredEntry = CONTAINING_RECORD( listEntry,
  421. PREFERRED_ENTRY,
  422. PreferredLinkage );
  423. RemoveEntryList( &preferredEntry->PreferredLinkage );
  424. OmDereferenceObject( preferredEntry->PreferredNode );
  425. LocalFree( preferredEntry );
  426. }
  427. Group->OrderedOwners = 0;
  428. CL_ASSERT ( IsListEmpty(&Group->PreferredOwners) );
  429. status = DmQueryMultiSz( hGroupKey,
  430. CLUSREG_NAME_GRP_PREFERRED_OWNERS,
  431. &preferredOwnersString,
  432. &preferredOwnersStringMaxSize,
  433. &preferredOwnersStringSize );
  434. if ( status == NO_ERROR ) {
  435. //
  436. // Now Create the Preferred Owners list.
  437. //
  438. for ( mszStringIndex = 0; ; mszStringIndex++ ) {
  439. LPCWSTR nameString;
  440. PNM_NODE preferredNode;
  441. nameString = ClRtlMultiSzEnum( preferredOwnersString,
  442. preferredOwnersStringSize/sizeof(WCHAR),
  443. mszStringIndex );
  444. if ( nameString == NULL ) {
  445. break;
  446. }
  447. //
  448. // Create the Preferred Owners List entry
  449. //
  450. preferredEntry = LocalAlloc( LMEM_FIXED, sizeof(PREFERRED_ENTRY) );
  451. if ( preferredEntry == NULL ) {
  452. status = ERROR_NOT_ENOUGH_MEMORY;
  453. return(status);
  454. }
  455. //
  456. // Create the preferred owners. This will implicitly create
  457. // additional reference required for the preferred owner nodes.
  458. //
  459. ClRtlLogPrint(LOG_NOISE,
  460. "[FM] Group %1!ws! preferred owner %2!ws!.\n",
  461. OmObjectId(Group),
  462. nameString);
  463. preferredNode = OmReferenceObjectById( ObjectTypeNode,
  464. nameString );
  465. if ( preferredNode == NULL ) {
  466. LocalFree(preferredEntry);
  467. status = GetLastError();
  468. ClRtlLogPrint(LOG_NOISE,
  469. "[FM] Failed to find node %1!ws! for Group %2!ws!\n",
  470. nameString,
  471. OmObjectId(Group));
  472. } else {
  473. Group->OrderedOwners++;
  474. preferredEntry->PreferredNode = preferredNode;
  475. InsertTailList( &Group->PreferredOwners,
  476. &preferredEntry->PreferredLinkage );
  477. }
  478. }
  479. LocalFree( preferredOwnersString );
  480. }
  481. //
  482. // We now include all remaining nodes in the preferred owners list.
  483. //
  484. // Every node must maintain the same ordering for the preferred list
  485. // for the multi-node cluster to work
  486. //
  487. status = FmpEnumNodesById( 0, &pNmNodeEnum );
  488. if ( status != ERROR_SUCCESS )
  489. {
  490. CL_UNEXPECTED_ERROR( status );
  491. ClRtlLogPrint(LOG_UNUSUAL,
  492. "[FM] FmpQueryGroupNodes: FmpEnumNodesById failed, status = %1!u!\r\n",
  493. status);
  494. // return error
  495. }
  496. for ( i=0; i<pNmNodeEnum->NodeCount; i++ )
  497. {
  498. pNmNode = OmReferenceObjectById( ObjectTypeNode,
  499. pNmNodeEnum->NodeList[i].NodeId );
  500. CL_ASSERT( pNmNode != NULL );
  501. FmpAddNodeToPrefList( pNmNode, Group );
  502. OmDereferenceObject( pNmNode );
  503. }
  504. //
  505. // Now prune out all the unreachable nodes.
  506. //
  507. FmpPruneGroupOwners( Group );
  508. //
  509. // Chittur Subbaraman (chitturs) - 12/11/98
  510. //
  511. // Free the memory allocated for pNmNodeEnum.
  512. // (Fix memory leak)
  513. //
  514. LocalFree( pNmNodeEnum );
  515. return( ERROR_SUCCESS );
  516. } // FmpQueryGroupNodes
  517. DWORD
  518. WINAPI
  519. FmpQueryGroupInfo(
  520. IN PVOID Object,
  521. IN BOOL Initialize
  522. )
  523. /*++
  524. Routine Description:
  525. Queries Group info from the registry when creating a Group Object.
  526. Arguments:
  527. Object - A pointer to the Group object being created.
  528. Initialize - TRUE if the resource objects should be initialized. FALSE
  529. otherwise.
  530. Return Value:
  531. ERROR_SUCCESS if successful.
  532. Win32 error code otherwise.
  533. --*/
  534. {
  535. PFM_GROUP Group = (PFM_GROUP)Object;
  536. PFM_RESOURCE Resource;
  537. DWORD status;
  538. LPWSTR containsString = NULL;
  539. DWORD containsStringSize = 0;
  540. DWORD containsStringMaxSize = 0;
  541. DWORD temp;
  542. DWORD mszStringIndex;
  543. DWORD failoverThreshold = CLUSTER_GROUP_DEFAULT_FAILOVER_THRESHOLD;
  544. DWORD failoverPeriod = CLUSTER_GROUP_DEFAULT_FAILOVER_PERIOD;
  545. DWORD autoFailbackType = CLUSTER_GROUP_DEFAULT_AUTO_FAILBACK_TYPE;
  546. DWORD zero = 0;
  547. PLIST_ENTRY listEntry;
  548. HDMKEY groupKey;
  549. DWORD groupNameStringMaxSize = 0;
  550. DWORD groupNameStringSize = 0;
  551. LPWSTR groupName;
  552. PPREFERRED_ENTRY preferredEntry;
  553. DWORD dwBufferSize = 0;
  554. DWORD dwStringSize;
  555. //
  556. // Initialize the Group object from the registry info.
  557. //
  558. if ( Group->Initialized ) {
  559. return(ERROR_SUCCESS);
  560. }
  561. ClRtlLogPrint(LOG_NOISE,
  562. "[FM] Initializing group %1!ws! from the registry.\n",
  563. OmObjectId(Group));
  564. //
  565. // Open the group key.
  566. //
  567. groupKey = DmOpenKey( DmGroupsKey,
  568. OmObjectId(Group),
  569. MAXIMUM_ALLOWED );
  570. if ( groupKey == NULL ) {
  571. status = GetLastError();
  572. ClRtlLogPrint(LOG_UNUSUAL,
  573. "[FM] Unable to open group key %1!ws!, %2!u!\n",
  574. OmObjectId(Group),
  575. status);
  576. return(status);
  577. }
  578. //
  579. // Read the required group values. The strings will be allocated
  580. // by the DmQuery* functions.
  581. //
  582. //
  583. // Get the Name.
  584. //
  585. status = DmQuerySz( groupKey,
  586. CLUSREG_NAME_GRP_NAME,
  587. &groupName,
  588. &groupNameStringMaxSize,
  589. &groupNameStringSize );
  590. if (status != ERROR_SUCCESS) {
  591. ClRtlLogPrint(LOG_NOISE,
  592. "[FM] Unable to read name for Group %1!ws!\n",
  593. OmObjectId(Group));
  594. goto error_exit;
  595. }
  596. status = OmSetObjectName( Group, groupName );
  597. if ( status != ERROR_SUCCESS ) {
  598. ClRtlLogPrint(LOG_ERROR,
  599. "[FM] Unable to set name %1!ws! for group %2!ws!, error %3!u!.\n",
  600. groupName,
  601. OmObjectId(Group),
  602. status );
  603. goto error_exit;
  604. }
  605. ClRtlLogPrint(LOG_NOISE,
  606. "[FM] Name for Group %1!ws! is '%2!ws!'.\n",
  607. OmObjectId(Group),
  608. groupName);
  609. LocalFree(groupName);
  610. //
  611. // Get the PersistentState.
  612. //
  613. status = DmQueryDword( groupKey,
  614. CLUSREG_NAME_GRP_PERSISTENT_STATE,
  615. &temp,
  616. &zero );
  617. //
  618. // If the group state is non-zero then we go online.
  619. //
  620. if ( temp ) {
  621. Group->PersistentState = ClusterGroupOnline;
  622. } else {
  623. Group->PersistentState = ClusterGroupOffline;
  624. }
  625. //
  626. // Get the OPTIONAL PreferredOwners list.
  627. // *** NOTE *** This MUST be done before processing the contains list!
  628. //
  629. status = FmpQueryGroupNodes(Group, groupKey);
  630. if (status != ERROR_SUCCESS) {
  631. ClRtlLogPrint(LOG_UNUSUAL,"[FM] Error %1!d! creating preferred owners list\n",status);
  632. goto error_exit;
  633. }
  634. //
  635. // Get the Contains string.
  636. //
  637. status = DmQueryMultiSz( groupKey,
  638. CLUSREG_NAME_GRP_CONTAINS,
  639. &containsString,
  640. &containsStringMaxSize,
  641. &containsStringSize );
  642. if ( status != NO_ERROR ) {
  643. if ( status != ERROR_FILE_NOT_FOUND ) {
  644. ClRtlLogPrint(LOG_UNUSUAL,
  645. "[FM] Unable to read Contains for group %1!ws!\n",
  646. OmObjectId(Group));
  647. }
  648. } else {
  649. //
  650. // Now Create the Contains list.
  651. //
  652. for ( mszStringIndex = 0; ; mszStringIndex++ ) {
  653. LPCWSTR nameString;
  654. PFM_RESOURCE containedResource;
  655. nameString = ClRtlMultiSzEnum( containsString,
  656. containsStringSize/sizeof(WCHAR),
  657. mszStringIndex );
  658. if ( nameString == NULL ) {
  659. break;
  660. }
  661. ClRtlLogPrint(LOG_NOISE,
  662. "[FM] Group %1!ws! contains Resource %2!ws!.\n",
  663. OmObjectId(Group),
  664. nameString);
  665. //
  666. // Try to create the object.
  667. //
  668. FmpAcquireResourceLock();
  669. FmpAcquireLocalGroupLock( Group );
  670. containedResource = FmpCreateResource( Group,
  671. nameString,
  672. NULL,
  673. Initialize );
  674. FmpReleaseLocalGroupLock( Group );
  675. FmpReleaseResourceLock();
  676. //
  677. // Check if we got a resource.
  678. //
  679. if ( containedResource == NULL ) {
  680. //
  681. // This group claims to contain a non-existent resource.
  682. // Log an error, but keep going. This should not tank the
  683. // whole group. Also, let the arbitration code know about
  684. // the failure of a resource.
  685. //
  686. Group->InitFailed = TRUE;
  687. ClRtlLogPrint(LOG_UNUSUAL,
  688. "[FM] Failed to find resource %1!ws! for Group %2!ws!\n",
  689. nameString,
  690. OmObjectId(Group));
  691. }
  692. }
  693. LocalFree(containsString);
  694. }
  695. //
  696. // Get the AutoFailbackType.
  697. //
  698. status = DmQueryDword( groupKey,
  699. CLUSREG_NAME_GRP_FAILBACK_TYPE,
  700. &temp,
  701. &autoFailbackType );
  702. //
  703. // Verify that AutoFailbackType is okay.
  704. //
  705. if ( temp >= FailbackMaximum ) {
  706. ClRtlLogPrint(LOG_NOISE,
  707. "[FM] Illegal value for AutoFailbackType on %1!ws!, setting to default\n",
  708. OmObjectId(Group));
  709. temp = autoFailbackType;
  710. }
  711. Group->FailbackType = (UCHAR)temp;
  712. //
  713. // Get the FailbackWindowStart.
  714. //
  715. status = DmQueryDword( groupKey,
  716. CLUSREG_NAME_GRP_FAILBACK_WIN_START,
  717. &temp,
  718. &zero );
  719. //
  720. // Verify that FailbackWindowStart is okay.
  721. //
  722. if ( temp > 24 ) {
  723. if ( temp != CLUSTER_GROUP_DEFAULT_FAILBACK_WINDOW_START ) {
  724. ClRtlLogPrint(LOG_NOISE,
  725. "[FM] Illegal value for FailbackWindowStart on %1!ws!,setting to default\n",
  726. OmObjectId(Group));
  727. temp = zero;
  728. }
  729. }
  730. Group->FailbackWindowStart = (UCHAR)temp;
  731. //
  732. // Get the FailbackWindowEnd.
  733. //
  734. status = DmQueryDword( groupKey,
  735. CLUSREG_NAME_GRP_FAILBACK_WIN_END,
  736. &temp,
  737. &zero );
  738. //
  739. // Verify that FailbackWindowEnd is okay.
  740. //
  741. if ( temp > 24 ) {
  742. if ( temp != CLUSTER_GROUP_DEFAULT_FAILBACK_WINDOW_END ) {
  743. ClRtlLogPrint(LOG_NOISE,
  744. "[FM] Illegal value for FailbackWindowEnd on %1!ws!, setting to default\n",
  745. OmObjectId(Group));
  746. temp = zero;
  747. }
  748. }
  749. Group->FailbackWindowEnd = (UCHAR)temp;
  750. //
  751. // Get the FailoverPeriod.
  752. //
  753. status = DmQueryDword( groupKey,
  754. CLUSREG_NAME_GRP_FAILOVER_PERIOD,
  755. &temp,
  756. &failoverPeriod );
  757. //
  758. // Verify that FailoverPeriod is okay. Take any value up to UCHAR max.
  759. // In theory we could take any value... but in practice we have to convert
  760. // this time to milliseconds (currently). That means that 1193 hours can
  761. // fit in a DWORD - so that is the maximum we can take. (We are limited
  762. // because we use GetTickCount, which returns a DWORD in milliseconds.)
  763. //
  764. if ( temp > CLUSTER_GROUP_MAXIMUM_FAILOVER_PERIOD ) { // Keep it positive?
  765. ClRtlLogPrint(LOG_NOISE,
  766. "[FM] Illegal value for FailolverPeriod on %1!ws!. Max is 1193\n",
  767. OmObjectId(Group));
  768. temp = failoverPeriod;
  769. }
  770. Group->FailoverPeriod = (UCHAR)temp;
  771. //
  772. // Get the FailoverThreshold.
  773. //
  774. status = DmQueryDword( groupKey,
  775. CLUSREG_NAME_GRP_FAILOVER_THRESHOLD,
  776. &(Group->FailoverThreshold),
  777. &failoverThreshold );
  778. //
  779. // Verify that FailoverThreshold is okay. Take any value.
  780. //
  781. //
  782. // Get the AntiAffinityClassName property if present.
  783. //
  784. status = DmQueryMultiSz( groupKey,
  785. CLUSREG_NAME_GRP_ANTI_AFFINITY_CLASS_NAME,
  786. &Group->lpszAntiAffinityClassName,
  787. &dwBufferSize,
  788. &dwStringSize );
  789. //
  790. // Handle the case in which the string is empty.
  791. //
  792. if ( ( status == ERROR_SUCCESS ) &&
  793. ( Group->lpszAntiAffinityClassName != NULL ) &&
  794. ( Group->lpszAntiAffinityClassName[0] == L'\0' ) )
  795. {
  796. LocalFree( Group->lpszAntiAffinityClassName );
  797. Group->lpszAntiAffinityClassName = NULL;
  798. }
  799. //
  800. // We're done. We should only get here if Group->Initialized is FALSE.
  801. //
  802. CL_ASSERT( Group->Initialized == FALSE );
  803. Group->Initialized = TRUE;
  804. Group->RegistryKey = groupKey;
  805. //
  806. // Now register for any changes to the resource key.
  807. //
  808. status = DmNotifyChangeKey(
  809. groupKey,
  810. (DWORD) CLUSTER_CHANGE_ALL,
  811. FALSE, // Only watch the top of the tree
  812. &Group->DmRundownList,
  813. FmpGroupChangeCallback,
  814. (DWORD_PTR)Group,
  815. 0 );
  816. if ( status != ERROR_SUCCESS) {
  817. ClRtlLogPrint(LOG_NOISE,
  818. "[FM] Error registering for DM change notify on group %1!ws!, error %2!u!.\n",
  819. OmObjectId(Group),
  820. status);
  821. goto error_exit;
  822. }
  823. return(ERROR_SUCCESS);
  824. error_exit:
  825. Group->Initialized = FALSE;
  826. Group->RegistryKey = NULL;
  827. DmCloseKey(groupKey);
  828. //
  829. // Cleanup any contained resources
  830. //
  831. while ( !IsListEmpty(&Group->Contains) ) {
  832. listEntry = RemoveHeadList(&Group->Contains);
  833. Resource = CONTAINING_RECORD(listEntry, FM_RESOURCE, ContainsLinkage);
  834. OmDereferenceObject(Resource);
  835. }
  836. //
  837. // Cleanup any preferred nodes
  838. //
  839. while ( !IsListEmpty(&Group->PreferredOwners) ) {
  840. listEntry = RemoveHeadList(&Group->PreferredOwners);
  841. preferredEntry = CONTAINING_RECORD(listEntry, PREFERRED_ENTRY, PreferredLinkage);
  842. OmDereferenceObject(preferredEntry->PreferredNode);
  843. LocalFree(preferredEntry);
  844. }
  845. return(status);
  846. } // FmpQueryGroupInfo
  847. DWORD
  848. WINAPI
  849. FmpFixupGroupInfo(
  850. IN PFM_GROUP Group
  851. )
  852. /*++
  853. Routine Description:
  854. Re-queries Group info from the registry to fixup items that may have
  855. changed since the quorum resource (and the Group that it is in) was
  856. first created.
  857. This routine exists because we may have created the quorum resource
  858. (and its Group) early in the 'life' of the cluster, before all the node
  859. objects (for example) were created. We then would have failed generating
  860. the list of possible owners for the resource. This in turn would have
  861. caused some entries from the preferred list to get pruned. We need to
  862. redo this operation again here.
  863. Arguments:
  864. Group - A pointer to the Group object to fix up.
  865. Return Value:
  866. ERROR_SUCCESS if successful.
  867. Win32 error code otherwise.
  868. Notes:
  869. It is assumed that the quorum resource fixup already has happened.
  870. --*/
  871. {
  872. DWORD status;
  873. status = FmpQueryGroupNodes(Group, Group->RegistryKey);
  874. return(status);
  875. } // FmpFixupGroupInfo
  876. DWORD
  877. WINAPI
  878. FmpQueryResourceInfo(
  879. IN PVOID Object,
  880. IN BOOL Initialize
  881. )
  882. /*++
  883. Routine Description:
  884. Queries Resource info from the registry when creating a Resource Object.
  885. Arguments:
  886. Object - A pointer to the Resource object being created.
  887. Initialize - TRUE if the resource should be fully initialized.
  888. FALSE otherwise.
  889. Return Value:
  890. ERROR_SUCCESS if successful.
  891. Win32 error code otherwise.
  892. --*/
  893. {
  894. PFM_RESOURCE Resource = (PFM_RESOURCE)Object;
  895. DWORD status;
  896. DWORD dllNameStringSize = 0;
  897. DWORD dllNameStringMaxSize = 0;
  898. LPWSTR resourceTypeString = NULL;
  899. DWORD resourceTypeStringMaxSize = 0;
  900. DWORD resourceTypeStringSize = 0;
  901. DWORD dependenciesStringMaxSize = 0;
  902. DWORD restartThreshold = CLUSTER_RESOURCE_DEFAULT_RESTART_THRESHOLD;
  903. DWORD restartPeriod = CLUSTER_RESOURCE_DEFAULT_RESTART_PERIOD;
  904. DWORD pendingTimeout = CLUSTER_RESOURCE_DEFAULT_PENDING_TIMEOUT;
  905. DWORD RetryPeriodOnFailure = CLUSTER_RESOURCE_DEFAULT_RETRY_PERIOD_ON_FAILURE;
  906. DWORD defaultRestartAction = RestartGroup;
  907. DWORD DefaultExFlags = 0;
  908. DWORD zero = 0;
  909. DWORD temp;
  910. DWORD separateMonitor;
  911. HDMKEY resourceKey;
  912. DWORD resourceNameStringMaxSize = 0;
  913. DWORD resourceNameStringSize = 0;
  914. LPWSTR resourceName = NULL;
  915. LPWSTR possibleOwnersString = NULL;
  916. DWORD possibleOwnersStringSize = 0;
  917. DWORD possibleOwnersStringMaxSize = 0;
  918. DWORD mszStringIndex;
  919. PPOSSIBLE_ENTRY possibleEntry;
  920. PLIST_ENTRY listEntry;
  921. WCHAR unknownName[] = L"_Unknown9999";
  922. DWORD nameSize = 0;
  923. DWORD stringSize;
  924. //if the key is non null, this resource has already been initialized
  925. if (Resource->RegistryKey != NULL)
  926. return(ERROR_SUCCESS);
  927. ClRtlLogPrint(LOG_NOISE,
  928. "[FM] Initializing resource %1!ws! from the registry.\n",
  929. OmObjectId(Resource));
  930. //
  931. // Begin initializing the resource from the registry.
  932. //
  933. //
  934. // Open the resource key.
  935. //
  936. resourceKey = DmOpenKey( DmResourcesKey,
  937. OmObjectId(Resource),
  938. MAXIMUM_ALLOWED );
  939. if ( resourceKey == NULL ) {
  940. status = GetLastError();
  941. ClRtlLogPrint(LOG_NOISE,
  942. "[FM] Unable to open resource key %1!ws!, %2!u!\n",
  943. OmObjectId(Resource),
  944. status);
  945. return(ERROR_INVALID_NAME);
  946. }
  947. //
  948. // Read the required resource values. The strings will be allocated
  949. // by the DmQuery* functions.
  950. //
  951. //
  952. // Get the Name.
  953. //
  954. status = DmQuerySz( resourceKey,
  955. CLUSREG_NAME_RES_NAME,
  956. &resourceName,
  957. &resourceNameStringMaxSize,
  958. &resourceNameStringSize );
  959. if (status != ERROR_SUCCESS) {
  960. ClRtlLogPrint(LOG_NOISE,
  961. "[FM] Unable to read name for resource %1!ws!\n",
  962. OmObjectId(Resource));
  963. if ( OmObjectName( Resource ) == NULL ) {
  964. wsprintf( unknownName,
  965. L"_Unknown%u",
  966. InterlockedIncrement( &FmpUnknownCount ));
  967. status = OmSetObjectName( Resource, unknownName );
  968. } else {
  969. status = ERROR_SUCCESS;
  970. }
  971. } else {
  972. status = OmSetObjectName( Resource, resourceName );
  973. }
  974. if ( status != ERROR_SUCCESS ) {
  975. ClRtlLogPrint(LOG_ERROR,
  976. "[FM] Unable to set name %1!ws! for resource %2!ws!, error %3!u!.\n",
  977. resourceName,
  978. OmObjectId(Resource),
  979. status );
  980. LocalFree(resourceName);
  981. status = ERROR_INVALID_NAME;
  982. goto error_exit;
  983. }
  984. ClRtlLogPrint(LOG_NOISE,
  985. "[FM] Name for Resource %1!ws! is '%2!ws!'.\n",
  986. OmObjectId(Resource),
  987. resourceName);
  988. LocalFree(resourceName);
  989. //
  990. // Get the dependencies list.
  991. //
  992. status = DmQueryMultiSz( resourceKey,
  993. CLUSREG_NAME_RES_DEPENDS_ON,
  994. &(Resource->Dependencies),
  995. &dependenciesStringMaxSize,
  996. &(Resource->DependenciesSize) );
  997. if (status != NO_ERROR) {
  998. if ( status != ERROR_FILE_NOT_FOUND ) {
  999. ClRtlLogPrint(LOG_NOISE,
  1000. "[FM] Unable to read Dependencies for resource %1!ws!\n",
  1001. OmObjectId(Resource));
  1002. }
  1003. }
  1004. //
  1005. // Get the OPTIONAL PossibleOwners list.
  1006. //
  1007. // We do this here, because we must have a possible owners list for the
  1008. // CluAdmin to start the resource.
  1009. //
  1010. status = DmQueryMultiSz( resourceKey,
  1011. CLUSREG_NAME_RES_POSSIBLE_OWNERS,
  1012. &possibleOwnersString,
  1013. &possibleOwnersStringMaxSize,
  1014. &possibleOwnersStringSize );
  1015. if ( status == NO_ERROR ) {
  1016. //
  1017. // Now Create the Possible Owners list.
  1018. //
  1019. for ( mszStringIndex = 0; ; mszStringIndex++ ) {
  1020. LPCWSTR nameString;
  1021. PNM_NODE possibleNode;
  1022. nameString = ClRtlMultiSzEnum( possibleOwnersString,
  1023. possibleOwnersStringSize/sizeof(WCHAR),
  1024. mszStringIndex );
  1025. if ( nameString == NULL ) {
  1026. break;
  1027. }
  1028. possibleNode = OmReferenceObjectById( ObjectTypeNode,
  1029. nameString );
  1030. if ( possibleNode == NULL ) {
  1031. ClRtlLogPrint(LOG_NOISE,
  1032. "[FM] Warning, failed to find node %1!ws! for Resource %2!ws!\n",
  1033. nameString,
  1034. OmObjectId(Resource));
  1035. } else {
  1036. Resource->PossibleList = TRUE;
  1037. status = FmpAddPossibleEntry(Resource, possibleNode);
  1038. OmDereferenceObject(possibleNode);
  1039. if (status != ERROR_SUCCESS) {
  1040. goto error_exit;
  1041. }
  1042. }
  1043. }
  1044. LocalFree(possibleOwnersString);
  1045. //
  1046. // Now prune out unusable nodes from the preferred owners list.
  1047. //
  1048. FmpPrunePreferredList( Resource );
  1049. } else {
  1050. //
  1051. // No possible owners value was specified. Add all the nodes
  1052. // to the possible owners list. Note there is no point in pruning
  1053. // the preferred list after this since this resource can run
  1054. // anywhere.
  1055. //
  1056. OmEnumObjects( ObjectTypeNode,
  1057. FmpEnumAddAllOwners,
  1058. Resource,
  1059. NULL );
  1060. }
  1061. //
  1062. // Get the resource type.
  1063. //
  1064. status = DmQuerySz( resourceKey,
  1065. CLUSREG_NAME_RES_TYPE,
  1066. &resourceTypeString,
  1067. &resourceTypeStringMaxSize,
  1068. &resourceTypeStringSize );
  1069. if (status != ERROR_SUCCESS) {
  1070. ClRtlLogPrint(LOG_NOISE,
  1071. "[FM] Unable to read ResourceType for resource %1!ws!\n",
  1072. OmObjectId(Resource));
  1073. goto error_exit;
  1074. }
  1075. //
  1076. // Open (and reference) the resource type.
  1077. //
  1078. if (Resource->Type == NULL)
  1079. {
  1080. Resource->Type = OmReferenceObjectById( ObjectTypeResType,
  1081. resourceTypeString );
  1082. }
  1083. if (Resource->Type == NULL) {
  1084. PFM_RESTYPE pResType;
  1085. //
  1086. // If we can't find a resource type, then try to create it.
  1087. //
  1088. pResType = FmpCreateResType(resourceTypeString );
  1089. if (pResType == NULL) {
  1090. status = ERROR_INVALID_PARAMETER;
  1091. LocalFree(resourceTypeString);
  1092. goto error_exit;
  1093. }
  1094. //bump the ref count before saving a pointer to it in the
  1095. //resource structure.
  1096. OmReferenceObject(pResType);
  1097. Resource->Type = pResType;
  1098. }
  1099. LocalFree(resourceTypeString);
  1100. if ( !Initialize ) {
  1101. //
  1102. // We're not supposed to fully initialize the resource. This is
  1103. // when we're early in the init process. We need to keep the registry
  1104. // key closed when leaving.
  1105. //
  1106. DmCloseKey(resourceKey);
  1107. return(ERROR_SUCCESS);
  1108. }
  1109. //
  1110. // Get the IsAlive poll interval
  1111. //
  1112. CL_ASSERT( Resource->Type->IsAlivePollInterval != 0 );
  1113. status = DmQueryDword( resourceKey,
  1114. CLUSREG_NAME_RES_IS_ALIVE,
  1115. &Resource->IsAlivePollInterval,
  1116. &Resource->Type->IsAlivePollInterval );
  1117. if ( status != NO_ERROR ) {
  1118. ClRtlLogPrint(LOG_NOISE,
  1119. "[FM] Unable to read IsAlivePollInterval for resource %1!ws!. Error %2!u!\n",
  1120. OmObjectId(Resource),
  1121. status);
  1122. goto error_exit;
  1123. }
  1124. if ( Resource->IsAlivePollInterval == CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL ) {
  1125. Resource->IsAlivePollInterval = Resource->Type->IsAlivePollInterval;
  1126. }
  1127. //
  1128. // Get the LooksAlive poll interval
  1129. //
  1130. CL_ASSERT( Resource->Type->LooksAlivePollInterval != 0 );
  1131. status = DmQueryDword( resourceKey,
  1132. CLUSREG_NAME_RES_LOOKS_ALIVE,
  1133. &Resource->LooksAlivePollInterval,
  1134. &Resource->Type->LooksAlivePollInterval );
  1135. if ( status != NO_ERROR ) {
  1136. ClRtlLogPrint(LOG_NOISE,
  1137. "[FM] Unable to read LooksAlivePollInterval for resource %1!ws!. Error %2!u!\n",
  1138. OmObjectId(Resource),
  1139. status);
  1140. goto error_exit;
  1141. }
  1142. if ( Resource->LooksAlivePollInterval == CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL ) {
  1143. Resource->LooksAlivePollInterval = Resource->Type->LooksAlivePollInterval;
  1144. }
  1145. //
  1146. // Get the current persistent state of the resource.
  1147. //
  1148. status = DmQueryDword( resourceKey,
  1149. CLUSREG_NAME_RES_PERSISTENT_STATE,
  1150. &temp,
  1151. NULL );
  1152. //
  1153. // Save the current resource state.
  1154. //
  1155. if ( ( status == ERROR_FILE_NOT_FOUND ) ||
  1156. ( ( status == ERROR_SUCCESS ) && ( temp == CLUSTER_RESOURCE_DEFAULT_PERSISTENT_STATE ) ) ) {
  1157. switch ( Resource->Group->PersistentState ) {
  1158. case ClusterGroupOnline:
  1159. Resource->PersistentState = ClusterResourceOnline;
  1160. break;
  1161. case ClusterGroupOffline:
  1162. Resource->PersistentState = ClusterResourceOffline;
  1163. break;
  1164. default:
  1165. break;
  1166. }
  1167. } else if ( status != NO_ERROR ) {
  1168. ClRtlLogPrint(LOG_NOISE,
  1169. "[FM] Unable to read PersistentState for resource %1!ws!. Error %2!u!\n",
  1170. OmObjectId(Resource),
  1171. status);
  1172. goto error_exit;
  1173. } else if ( temp ) {
  1174. Resource->PersistentState = ClusterResourceOnline;
  1175. } else {
  1176. Resource->PersistentState = ClusterResourceOffline;
  1177. }
  1178. //
  1179. // Determine the monitor to run this in.
  1180. //
  1181. status = DmQueryDword( resourceKey,
  1182. CLUSREG_NAME_RES_SEPARATE_MONITOR,
  1183. &separateMonitor,
  1184. &zero );
  1185. if ( separateMonitor ) {
  1186. Resource->Flags |= RESOURCE_SEPARATE_MONITOR;
  1187. }
  1188. //
  1189. // Get the RestartThreshold.
  1190. //
  1191. status = DmQueryDword( resourceKey,
  1192. CLUSREG_NAME_RES_RESTART_THRESHOLD,
  1193. &Resource->RestartThreshold,
  1194. &restartThreshold );
  1195. // Verify the RestartThreshold. Take any value.
  1196. //
  1197. // Get the RestartPeriod.
  1198. //
  1199. status = DmQueryDword( resourceKey,
  1200. CLUSREG_NAME_RES_RESTART_PERIOD,
  1201. &Resource->RestartPeriod,
  1202. &restartPeriod );
  1203. // Verify the RestartPeriod. Take any value.
  1204. //
  1205. // Get the RestartAction.
  1206. //
  1207. status = DmQueryDword( resourceKey,
  1208. CLUSREG_NAME_RES_RESTART_ACTION,
  1209. &Resource->RestartAction,
  1210. &defaultRestartAction );
  1211. // Verify the RestartAction.
  1212. if ( Resource->RestartAction >= RestartMaximum ) {
  1213. ClRtlLogPrint(LOG_NOISE,
  1214. "[FM] Illegal RestartAction for resource %1!ws!\n",
  1215. OmObjectId(Resource));
  1216. goto error_exit;
  1217. }
  1218. status = DmQueryDword( resourceKey,
  1219. CLUSREG_NAME_RES_RETRY_PERIOD_ON_FAILURE,
  1220. &Resource->RetryPeriodOnFailure,
  1221. &RetryPeriodOnFailure );
  1222. // make sure that RetryPeriodOnFailure >= RestartPeriod
  1223. if (Resource->RetryPeriodOnFailure < Resource->RestartPeriod)
  1224. {
  1225. ClRtlLogPrint(LOG_NOISE,
  1226. "[FM] Specified RetryPeriodOnFailure value is less than RestartPeriod value - setting RetryPeriodOnFailure equal to RestartPeriod \n");
  1227. Resource->RetryPeriodOnFailure = Resource->RestartPeriod;
  1228. }
  1229. //
  1230. // Get the extrinsic Flags
  1231. //
  1232. DefaultExFlags = 0;
  1233. status = DmQueryDword( resourceKey,
  1234. CLUSREG_NAME_FLAGS,
  1235. &Resource->ExFlags,
  1236. &DefaultExFlags );
  1237. if ( status != NO_ERROR ) {
  1238. ClRtlLogPrint(LOG_NOISE,
  1239. "[FM] Unable to read Extrinsic Flags for resource %1!ws!. Error %2!u!\n",
  1240. OmObjectId(Resource),
  1241. status);
  1242. goto error_exit;
  1243. }
  1244. //
  1245. // Get the PendingTimeout value.
  1246. //
  1247. status = DmQueryDword( resourceKey,
  1248. CLUSREG_NAME_RES_PENDING_TIMEOUT,
  1249. &Resource->PendingTimeout,
  1250. &pendingTimeout );
  1251. // Verify the PendingTimeout. Take any value.
  1252. //
  1253. // Now register for any changes to the resource key.
  1254. //
  1255. if (IsListEmpty(&Resource->DmRundownList))
  1256. {
  1257. status = DmNotifyChangeKey(
  1258. resourceKey,
  1259. (DWORD) CLUSTER_CHANGE_ALL,
  1260. FALSE, // Only watch the top of the tree
  1261. &Resource->DmRundownList,
  1262. FmpResourceChangeCallback,
  1263. (DWORD_PTR)Resource,
  1264. 0 );
  1265. if ( status != ERROR_SUCCESS) {
  1266. ClRtlLogPrint(LOG_NOISE,
  1267. "[FM] Error registering for DM change notify on resource %1!ws!, error %2!u!.\n",
  1268. OmObjectId(Resource),
  1269. status);
  1270. goto error_exit;
  1271. }
  1272. }
  1273. //
  1274. // Get the DebugPrefix string... this is on the resource type.
  1275. //
  1276. status = DmQuerySz( resourceKey,
  1277. CLUSREG_NAME_RES_DEBUG_PREFIX,
  1278. &Resource->DebugPrefix,
  1279. &nameSize,
  1280. &stringSize );
  1281. //
  1282. // Finally save the resource key for registry updates of the
  1283. // PersistentState.
  1284. //
  1285. Resource->RegistryKey = resourceKey;
  1286. return(ERROR_SUCCESS);
  1287. error_exit:
  1288. DmCloseKey(resourceKey);
  1289. if ( Resource->Type != NULL ) {
  1290. OmDereferenceObject(Resource->Type);
  1291. }
  1292. //
  1293. // Cleanup any dependencies
  1294. //
  1295. if ( Resource->Dependencies != NULL ) {
  1296. LocalFree(Resource->Dependencies);
  1297. Resource->Dependencies = NULL;
  1298. }
  1299. //
  1300. // Cleanup any possible nodes
  1301. //
  1302. while ( !IsListEmpty(&Resource->PossibleOwners) ) {
  1303. listEntry = RemoveHeadList(&Resource->PossibleOwners);
  1304. possibleEntry = CONTAINING_RECORD(listEntry, POSSIBLE_ENTRY, PossibleLinkage);
  1305. OmDereferenceObject(possibleEntry->PossibleNode);
  1306. LocalFree(possibleEntry);
  1307. }
  1308. return(status);
  1309. } // FmpQueryResourceInfo
  1310. DWORD
  1311. WINAPI
  1312. FmpFixupResourceInfo(
  1313. IN PFM_RESOURCE Resource
  1314. )
  1315. /*++
  1316. Routine Description:
  1317. Re-queries Resource info from the registry to fixup items that may have
  1318. changed since the quorum resource was first created.
  1319. This routine exists because we may have created the quorum resource early
  1320. in the 'life' of the cluster, before all the node objects (for example)
  1321. were created. We then would have failed generating the list of possible
  1322. owners for the resource. In FmpQueryResourceInfo, we treat failures to
  1323. find node objects as non-fatal errors, which we will now cleanup.
  1324. Arguments:
  1325. Resource - A pointer to the Resource object to fix up.
  1326. Return Value:
  1327. ERROR_SUCCESS if successful.
  1328. Win32 error code otherwise.
  1329. --*/
  1330. {
  1331. LPWSTR possibleOwnersString = NULL;
  1332. DWORD possibleOwnersStringSize = 0;
  1333. DWORD possibleOwnersStringMaxSize = 0;
  1334. DWORD mszStringIndex;
  1335. DWORD status;
  1336. if ( Resource->RegistryKey == NULL ) {
  1337. return(ERROR_NOT_READY);
  1338. }
  1339. //
  1340. // Get the OPTIONAL PossibleOwners list.
  1341. //
  1342. status = DmQueryMultiSz( Resource->RegistryKey,
  1343. CLUSREG_NAME_RES_POSSIBLE_OWNERS,
  1344. &possibleOwnersString,
  1345. &possibleOwnersStringMaxSize,
  1346. &possibleOwnersStringSize );
  1347. if ( status == NO_ERROR ) {
  1348. //
  1349. // Now Create the Possible Owners list.
  1350. //
  1351. for ( mszStringIndex = 0; ; mszStringIndex++ ) {
  1352. LPCWSTR nameString;
  1353. PNM_NODE possibleNode;
  1354. nameString = ClRtlMultiSzEnum( possibleOwnersString,
  1355. possibleOwnersStringSize/sizeof(WCHAR),
  1356. mszStringIndex );
  1357. if ( nameString == NULL ) {
  1358. break;
  1359. }
  1360. possibleNode = OmReferenceObjectById( ObjectTypeNode,
  1361. nameString );
  1362. if ( possibleNode == NULL ) {
  1363. ClRtlLogPrint(LOG_NOISE,
  1364. "[FM] Warning, failed to find node %1!ws! for Resource %2!ws!\n",
  1365. nameString,
  1366. OmObjectId(Resource));
  1367. } else {
  1368. Resource->PossibleList = TRUE;
  1369. status = FmpAddPossibleEntry(Resource, possibleNode);
  1370. OmDereferenceObject(possibleNode);
  1371. if (status != ERROR_SUCCESS) {
  1372. return(status);
  1373. }
  1374. }
  1375. }
  1376. LocalFree(possibleOwnersString);
  1377. //
  1378. // Now prune out unusable nodes from the preferred owners list.
  1379. //
  1380. FmpPrunePreferredList( Resource );
  1381. } else {
  1382. //
  1383. // No possible owners value was specified. Add all the nodes
  1384. // to the possible owners list. Note there is no point in pruning
  1385. // the preferred list after this since this resource can run
  1386. // anywhere.
  1387. //
  1388. OmEnumObjects( ObjectTypeNode,
  1389. FmpEnumAddAllOwners,
  1390. Resource,
  1391. NULL );
  1392. }
  1393. return(ERROR_SUCCESS);
  1394. } // FmpFixupQuorumResourceInfo
  1395. DWORD
  1396. WINAPI
  1397. FmpQueryResTypeInfo(
  1398. IN PVOID Object
  1399. )
  1400. /*++
  1401. Routine Description:
  1402. Queries Resource Type info from the registry when creating a ResType Object.
  1403. Arguments:
  1404. Object - A pointer to the Resource Type object being created.
  1405. Return Value:
  1406. ERROR_SUCCESS if successful.
  1407. Win32 error code otherwise.
  1408. --*/
  1409. {
  1410. PFM_RESTYPE resType = (PFM_RESTYPE)Object;
  1411. DWORD status;
  1412. DWORD dwSize = 0;
  1413. DWORD stringSize;
  1414. HDMKEY resTypeKey;
  1415. DWORD temp;
  1416. LPWSTR pmszPossibleNodes = NULL;
  1417. //
  1418. // Open the resource type key.
  1419. //
  1420. resTypeKey = DmOpenKey( DmResourceTypesKey,
  1421. OmObjectId(resType),
  1422. MAXIMUM_ALLOWED );
  1423. if ( resTypeKey == NULL ) {
  1424. status = GetLastError();
  1425. ClRtlLogPrint(LOG_NOISE,
  1426. "[FM] Unable to open resource type key %1!ws!, %2!u!\n",
  1427. OmObjectId(resType),
  1428. status);
  1429. return(status);
  1430. }
  1431. //
  1432. // Read the required resource type DLL name. The strings will be allocated
  1433. // by the DmQuery* functions.
  1434. //
  1435. status = DmQuerySz( resTypeKey,
  1436. CLUSREG_NAME_RESTYPE_DLL_NAME,
  1437. &resType->DllName,
  1438. &dwSize,
  1439. &stringSize );
  1440. if ( status != NO_ERROR ) {
  1441. if ( status == ERROR_FILE_NOT_FOUND ) {
  1442. ClRtlLogPrint(LOG_CRITICAL,
  1443. "[FM] The DllName value for the %1!ws! resource type does not exist. "
  1444. "Resources of this type will not be monitored.\n",
  1445. OmObjectId(resType));
  1446. }
  1447. else {
  1448. ClRtlLogPrint(LOG_CRITICAL,
  1449. "[FM] The DllName value for the %1!ws! resource type could not be read "
  1450. "from the registry. Resources of this type will not be monitored. "
  1451. "The error was %2!d!.\n",
  1452. OmObjectId(resType),
  1453. status);
  1454. }
  1455. goto error_exit;
  1456. }
  1457. //
  1458. // Get the optional LooksAlive poll interval
  1459. //
  1460. status = DmQueryDword( resTypeKey,
  1461. CLUSREG_NAME_RESTYPE_LOOKS_ALIVE,
  1462. &resType->LooksAlivePollInterval,
  1463. NULL );
  1464. if ( status != NO_ERROR ) {
  1465. if ( status == ERROR_FILE_NOT_FOUND ) {
  1466. resType->LooksAlivePollInterval = CLUSTER_RESTYPE_DEFAULT_LOOKS_ALIVE;
  1467. } else {
  1468. ClRtlLogPrint(LOG_CRITICAL,
  1469. "[FM] The LooksAlive poll interval for the %1!ws! resource type could "
  1470. "not be read from the registry. Resources of this type will not be "
  1471. "monitored. The error was %2!d!.\n",
  1472. OmObjectId(resType),
  1473. status);
  1474. goto error_exit;
  1475. }
  1476. }
  1477. //
  1478. // Get the optional IsAlive poll interval
  1479. //
  1480. status = DmQueryDword( resTypeKey,
  1481. CLUSREG_NAME_RESTYPE_IS_ALIVE,
  1482. &resType->IsAlivePollInterval,
  1483. NULL );
  1484. if ( status != NO_ERROR ) {
  1485. if ( status == ERROR_FILE_NOT_FOUND ) {
  1486. resType->IsAlivePollInterval = CLUSTER_RESTYPE_DEFAULT_IS_ALIVE;
  1487. } else {
  1488. ClRtlLogPrint(LOG_CRITICAL,
  1489. "[FM] The IsAlive poll interval for the %1!ws! resource type "
  1490. "could not be read from the registry. Resources of this type "
  1491. "will not be monitored. The error was %2!d!.\n",
  1492. OmObjectId(resType),
  1493. status);
  1494. goto error_exit;
  1495. }
  1496. }
  1497. //
  1498. // Get the optional DebugPrefix string... this is on the resource type.
  1499. //
  1500. dwSize = 0;
  1501. status = DmQuerySz( resTypeKey,
  1502. CLUSREG_NAME_RESTYPE_DEBUG_PREFIX,
  1503. &resType->DebugPrefix,
  1504. &dwSize,
  1505. &stringSize );
  1506. //
  1507. // Get the optional DebugControlFunctions registry value
  1508. //
  1509. resType->Flags &= ~RESTYPE_DEBUG_CONTROL_FUNC;
  1510. temp = 0;
  1511. status = DmQueryDword( resTypeKey,
  1512. CLUSREG_NAME_RESTYPE_DEBUG_CTRLFUNC,
  1513. &temp,
  1514. NULL );
  1515. if ( status != NO_ERROR ) {
  1516. if ( status != ERROR_FILE_NOT_FOUND ) {
  1517. ClRtlLogPrint(LOG_CRITICAL,
  1518. "[FM] The Debug control functions for the %1!ws! resource type "
  1519. "could not be read from the registry. Resources of this type "
  1520. "will not be monitored. The error was %2!d!.\n",
  1521. OmObjectId(resType),
  1522. status);
  1523. goto error_exit;
  1524. }
  1525. }
  1526. if ( temp ) {
  1527. resType->Flags |= RESTYPE_DEBUG_CONTROL_FUNC;
  1528. }
  1529. //ss: bug make sure you free the old memory
  1530. InitializeListHead(&(resType->PossibleNodeList));
  1531. //
  1532. // Get the Possible Nodes
  1533. //
  1534. dwSize = 0;
  1535. status = DmQueryMultiSz( resTypeKey,
  1536. CLUSREG_NAME_RESTYPE_POSSIBLE_NODES,
  1537. &pmszPossibleNodes,
  1538. &dwSize,
  1539. &stringSize);
  1540. if ( status != NO_ERROR )
  1541. {
  1542. //if the possible node list is not found this is ok
  1543. if ( status != ERROR_FILE_NOT_FOUND )
  1544. {
  1545. ClRtlLogPrint(LOG_CRITICAL,
  1546. "[FM] The Possible nodes list for the %1!ws! resource type "
  1547. "could not be read from the registry. Resources of this type "
  1548. "will not be monitored. The error was %2!d!.\n",
  1549. OmObjectId(resType),
  1550. status);
  1551. goto error_exit;
  1552. }
  1553. }
  1554. ClRtlLogPrint(LOG_NOISE,
  1555. "[FM] FmpQueryResTypeInfo: Calling FmpAddPossibleNodeToList for restype %1!ws!\r\n",
  1556. OmObjectId(resType));
  1557. status = FmpAddPossibleNodeToList(pmszPossibleNodes, stringSize,
  1558. &resType->PossibleNodeList);
  1559. if ( status != ERROR_SUCCESS )
  1560. {
  1561. ClRtlLogPrint(LOG_CRITICAL,
  1562. "[FM] FmpCreateResType: FmpAddPossibleNodeToList() failed, status=%1!u!\r\n",
  1563. status);
  1564. goto error_exit;
  1565. }
  1566. error_exit:
  1567. if (pmszPossibleNodes) LocalFree(pmszPossibleNodes);
  1568. DmCloseKey(resTypeKey);
  1569. return(status);
  1570. } // FmpQueryResTypeInfo
  1571. VOID
  1572. FmpGroupChangeCallback(
  1573. IN DWORD_PTR Context1,
  1574. IN DWORD_PTR Context2,
  1575. IN DWORD CompletionFilter,
  1576. IN LPCWSTR RelativeName
  1577. )
  1578. /*++
  1579. Routine Description:
  1580. This routine basically flushes our cached data for the given group.
  1581. Arguments:
  1582. Context1 - A pointer to the Group object that was modified.
  1583. Context2 - Not used.
  1584. CompletionFilter - Not used.
  1585. RelativeName - The registry path relative to the entry that was modified.
  1586. Not used.
  1587. Return Value:
  1588. None.
  1589. --*/
  1590. {
  1591. PFM_GROUP Group = (PFM_GROUP)Context1;
  1592. HDMKEY groupKey;
  1593. DWORD status;
  1594. DWORD temp;
  1595. BOOL notify = FALSE;
  1596. DWORD dwBufferSize = 0;
  1597. DWORD dwStringSize;
  1598. groupKey = Group->RegistryKey;
  1599. if ( groupKey == NULL ) {
  1600. return;
  1601. }
  1602. //
  1603. // Re-fetch all of the data for the group.
  1604. //
  1605. // Name changes are managed elsewhere.
  1606. // The Contains list is managed elsewhere.
  1607. //
  1608. //
  1609. // Get the OPTIONAL PreferredOwners list.
  1610. // *** NOTE *** This MUST be done before processing the contains list!
  1611. //
  1612. status = FmpQueryGroupNodes(Group, groupKey);
  1613. if (status != ERROR_SUCCESS) {
  1614. ClRtlLogPrint(LOG_UNUSUAL,"[FM] Error %1!d! refreshing preferred owners list\n",status);
  1615. }
  1616. //
  1617. // Get the AutoFailbackType.
  1618. //
  1619. temp = Group->FailbackType;
  1620. status = DmQueryDword( groupKey,
  1621. CLUSREG_NAME_GRP_FAILBACK_TYPE,
  1622. &temp,
  1623. &temp );
  1624. //
  1625. // Verify that AutoFailbackType is okay.
  1626. //
  1627. if ( temp >= FailbackMaximum ) {
  1628. ClRtlLogPrint(LOG_NOISE,
  1629. "[FM] Illegal refresh value for AutoFailbackType on %1!ws!\n",
  1630. OmObjectId(Group));
  1631. } else {
  1632. if ( (UCHAR)temp != Group->FailbackType ) {
  1633. notify = TRUE;
  1634. }
  1635. Group->FailbackType = (UCHAR)temp;
  1636. }
  1637. //
  1638. // Get the FailbackWindowStart.
  1639. //
  1640. temp = Group->FailbackWindowStart;
  1641. status = DmQueryDword( groupKey,
  1642. CLUSREG_NAME_GRP_FAILBACK_WIN_START,
  1643. &temp,
  1644. &temp );
  1645. //
  1646. // Verify that FailbackWindowStart is okay.
  1647. //
  1648. if ( temp > 24 ) {
  1649. ClRtlLogPrint(LOG_NOISE,
  1650. "[FM] Illegal refresh value for FailbackWindowStart on %1!ws!\n",
  1651. OmObjectId(Group));
  1652. } else {
  1653. if ( (UCHAR)temp != Group->FailbackWindowStart ) {
  1654. notify = TRUE;
  1655. }
  1656. Group->FailbackWindowStart = (UCHAR)temp;
  1657. }
  1658. //
  1659. // Get the FailbackWindowEnd.
  1660. //
  1661. temp = Group->FailbackWindowEnd;
  1662. status = DmQueryDword( groupKey,
  1663. CLUSREG_NAME_GRP_FAILBACK_WIN_END,
  1664. &temp,
  1665. &temp );
  1666. //
  1667. // Verify that FailbackWindowEnd is okay.
  1668. //
  1669. if ( temp > 24 ) {
  1670. ClRtlLogPrint(LOG_NOISE,
  1671. "[FM] Illegal refresh value for FailbackWindowEnd on %1!ws!\n",
  1672. OmObjectId(Group));
  1673. } else {
  1674. if ( (UCHAR)temp != Group->FailbackWindowEnd ) {
  1675. notify = TRUE;
  1676. }
  1677. Group->FailbackWindowEnd = (UCHAR)temp;
  1678. }
  1679. //
  1680. // Get the FailoverPeriod.
  1681. //
  1682. temp = Group->FailoverPeriod;
  1683. status = DmQueryDword( groupKey,
  1684. CLUSREG_NAME_GRP_FAILOVER_PERIOD,
  1685. &temp,
  1686. &temp );
  1687. //
  1688. // Verify that FailoverPeriod is okay. Take any value up to UCHAR max.
  1689. // In theory we could take any value... but in practice we have to convert
  1690. // this time to milliseconds (currently). That means that 1193 hours can
  1691. // fit in a DWORD - so that is the maximum we can take. (We are limited
  1692. // because we use GetTickCount, which returns a DWORD in milliseconds.)
  1693. //
  1694. if ( temp > (1193) ) { // we dont bother Keeping it positive?
  1695. ClRtlLogPrint(LOG_NOISE,
  1696. "[FM] Illegal refresh value for FailolverPeriod on %1!ws!. Max is 596\n",
  1697. OmObjectId(Group));
  1698. } else {
  1699. if ( (UCHAR)temp != Group->FailoverPeriod ) {
  1700. notify = TRUE;
  1701. }
  1702. Group->FailoverPeriod = (UCHAR)temp;
  1703. }
  1704. //
  1705. // Get the FailoverThreshold.
  1706. //
  1707. status = DmQueryDword( groupKey,
  1708. CLUSREG_NAME_GRP_FAILOVER_THRESHOLD,
  1709. &(Group->FailoverThreshold),
  1710. &(Group->FailoverThreshold) );
  1711. //
  1712. // Verify that FailoverThreshold is okay. Take any value.
  1713. //
  1714. //
  1715. // Get the current persistent state of the group.
  1716. //
  1717. if ( Group->PersistentState == ClusterGroupOnline ) {
  1718. temp = 1;
  1719. } else {
  1720. temp = 0;
  1721. }
  1722. status = DmQueryDword( groupKey,
  1723. CLUSREG_NAME_GRP_PERSISTENT_STATE,
  1724. &temp,
  1725. &temp );
  1726. //
  1727. // If the group state is non-zero then we go online.
  1728. //
  1729. // Don't bother with change notifications... they should happen elsewhere.
  1730. //
  1731. if ( temp ) {
  1732. if ( ClusterGroupOnline != Group->PersistentState ) {
  1733. //notify = TRUE;
  1734. }
  1735. Group->PersistentState = ClusterGroupOnline;
  1736. } else {
  1737. if ( ClusterGroupOffline != Group->PersistentState ) {
  1738. //notify = TRUE;
  1739. }
  1740. Group->PersistentState = ClusterGroupOffline;
  1741. }
  1742. //
  1743. // Get the AntiAffinityClassName property if present.
  1744. //
  1745. LocalFree( Group->lpszAntiAffinityClassName );
  1746. Group->lpszAntiAffinityClassName = NULL;
  1747. status = DmQueryMultiSz( groupKey,
  1748. CLUSREG_NAME_GRP_ANTI_AFFINITY_CLASS_NAME,
  1749. &Group->lpszAntiAffinityClassName,
  1750. &dwBufferSize,
  1751. &dwStringSize );
  1752. //
  1753. // Handle the case in which the string is empty.
  1754. //
  1755. if ( ( status == ERROR_SUCCESS ) &&
  1756. ( Group->lpszAntiAffinityClassName != NULL ) &&
  1757. ( Group->lpszAntiAffinityClassName[0] == L'\0' ) )
  1758. {
  1759. LocalFree( Group->lpszAntiAffinityClassName );
  1760. Group->lpszAntiAffinityClassName = NULL;
  1761. }
  1762. // We're done!
  1763. if ( !FmpShutdown &&
  1764. notify ) {
  1765. ClusterEvent( CLUSTER_EVENT_GROUP_PROPERTY_CHANGE, Group );
  1766. }
  1767. return;
  1768. } // FmpGroupChangeCallback
  1769. VOID
  1770. FmpResourceChangeCallback(
  1771. IN DWORD_PTR Context1,
  1772. IN DWORD_PTR Context2,
  1773. IN DWORD CompletionFilter,
  1774. IN LPCWSTR RelativeName
  1775. )
  1776. /*++
  1777. Routine Description:
  1778. This routine basically flushes our cached data for the given resource.
  1779. Arguments:
  1780. Context1 - A pointer to the resource object that was modified.
  1781. Context2 - Not used.
  1782. CompletionFilter - Not used.
  1783. RelativeName - The registry path relative to the entry that was modified.
  1784. Not used.
  1785. Return Value:
  1786. None.
  1787. --*/
  1788. {
  1789. PFM_RESOURCE Resource = (PFM_RESOURCE)Context1;
  1790. HDMKEY resourceKey;
  1791. DWORD status;
  1792. DWORD separateMonitor;
  1793. DWORD zero = 0;
  1794. DWORD temp;
  1795. BOOL notify = FALSE;
  1796. DWORD dwDefault;
  1797. resourceKey = Resource->RegistryKey;
  1798. if ( resourceKey == NULL ) {
  1799. return;
  1800. }
  1801. //
  1802. // Re-fetch all of the data for the resource.
  1803. //
  1804. // Name changes are managed elsewhere.
  1805. // The dependency list is managed elsewhere.
  1806. //
  1807. // We can't change the resource type here!
  1808. // We can't stop the resource to start it in a separate monitor either.
  1809. //
  1810. #if 0
  1811. //
  1812. // Get the Name.
  1813. //
  1814. status = DmQuerySz( resourceKey,
  1815. CLUSREG_NAME_RES_NAME,
  1816. &resourceName,
  1817. &resourceNameStringMaxSize,
  1818. &resourceNameStringSize );
  1819. if (status != ERROR_SUCCESS) {
  1820. ClRtlLogPrint(LOG_NOISE,
  1821. "[FM] Unable to read name for resource %1!ws!\n",
  1822. OmObjectId(Resource));
  1823. if ( OmObjectName( Resource ) == NULL ) {
  1824. wsprintf( unknownName,
  1825. L"_Unknown%u",
  1826. InterlockedIncrement( &FmpUnknownCount ));
  1827. status = OmSetObjectName( Resource, unknownName );
  1828. } else {
  1829. status = ERROR_SUCCESS;
  1830. }
  1831. } else {
  1832. status = OmSetObjectName( Resource, resourceName );
  1833. }
  1834. if ( status != ERROR_SUCCESS ) {
  1835. ClRtlLogPrint(LOG_ERROR,
  1836. "[FM] Unable to set name %1!ws! for resource %2!ws!, error %3!u!.\n",
  1837. resourceName,
  1838. OmObjectId(Resource),
  1839. status );
  1840. }
  1841. #endif
  1842. //
  1843. // Get the IsAlive poll interval
  1844. //
  1845. temp = Resource->IsAlivePollInterval;
  1846. dwDefault = CLUSTER_RESOURCE_DEFAULT_IS_ALIVE;
  1847. status = DmQueryDword( resourceKey,
  1848. CLUSREG_NAME_RES_IS_ALIVE,
  1849. &Resource->IsAlivePollInterval,
  1850. &dwDefault );
  1851. if ( status != NO_ERROR ) {
  1852. ClRtlLogPrint(LOG_NOISE,
  1853. "[FM] Unable to refresh IsAlivePollInterval for resource %1!ws!. Error %2!u!\n",
  1854. OmObjectId(Resource),
  1855. status);
  1856. } else {
  1857. CL_ASSERT( Resource->Type->IsAlivePollInterval != 0 );
  1858. if ( temp != Resource->IsAlivePollInterval ) {
  1859. notify = TRUE;
  1860. }
  1861. if ( Resource->IsAlivePollInterval == CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL ) {
  1862. Resource->IsAlivePollInterval = Resource->Type->IsAlivePollInterval;
  1863. }
  1864. }
  1865. //
  1866. // Get the LooksAlive poll interval
  1867. //
  1868. temp = Resource->LooksAlivePollInterval;
  1869. dwDefault = CLUSTER_RESOURCE_DEFAULT_LOOKS_ALIVE;
  1870. status = DmQueryDword( resourceKey,
  1871. CLUSREG_NAME_RES_LOOKS_ALIVE,
  1872. &Resource->LooksAlivePollInterval,
  1873. &dwDefault );
  1874. if ( status != NO_ERROR ) {
  1875. ClRtlLogPrint(LOG_NOISE,
  1876. "[FM] Unable to refresh LooksAlivePollInterval for resource %1!ws!. Error %2!u!\n",
  1877. OmObjectId(Resource),
  1878. status);
  1879. } else {
  1880. CL_ASSERT( Resource->Type->IsAlivePollInterval != 0 );
  1881. if ( temp != Resource->LooksAlivePollInterval ) {
  1882. notify = TRUE;
  1883. }
  1884. if ( Resource->LooksAlivePollInterval == CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL ) {
  1885. Resource->LooksAlivePollInterval = Resource->Type->LooksAlivePollInterval;
  1886. }
  1887. }
  1888. //
  1889. // Get the RestartThreshold.
  1890. //
  1891. temp = Resource->RestartThreshold;
  1892. dwDefault = CLUSTER_RESOURCE_DEFAULT_RESTART_THRESHOLD;
  1893. status = DmQueryDword( resourceKey,
  1894. CLUSREG_NAME_RES_RESTART_THRESHOLD,
  1895. &Resource->RestartThreshold,
  1896. &dwDefault);
  1897. // Verify the RestartThreshold. Take any value.
  1898. if ( (status == NO_ERROR) &&
  1899. (temp != Resource->RestartThreshold) ) {
  1900. notify = TRUE;
  1901. }
  1902. //
  1903. // Get the RestartPeriod.
  1904. //
  1905. temp = Resource->RestartPeriod;
  1906. dwDefault = CLUSTER_RESOURCE_DEFAULT_RESTART_PERIOD;
  1907. status = DmQueryDword( resourceKey,
  1908. CLUSREG_NAME_RES_RESTART_PERIOD,
  1909. &Resource->RestartPeriod,
  1910. &dwDefault );
  1911. if ( (status == NO_ERROR) &&
  1912. (temp != Resource->RestartPeriod) ) {
  1913. notify = TRUE;
  1914. }
  1915. // Verify the RestartPeriod. Take any value.
  1916. //
  1917. // Get the RestartAction.
  1918. //
  1919. temp = Resource->RestartAction;
  1920. dwDefault = CLUSTER_RESOURCE_DEFAULT_RESTART_ACTION;
  1921. status = DmQueryDword( resourceKey,
  1922. CLUSREG_NAME_RES_RESTART_ACTION,
  1923. &Resource->RestartAction,
  1924. &dwDefault);
  1925. // Verify the RestartAction.
  1926. if ( status == NO_ERROR ) {
  1927. if ( temp != Resource->RestartAction ) {
  1928. notify = TRUE;
  1929. }
  1930. if ( Resource->RestartAction >= RestartMaximum ) {
  1931. ClRtlLogPrint(LOG_NOISE,
  1932. "[FM] Illegal RestartAction refresh for resource %1!ws!\n",
  1933. OmObjectId(Resource));
  1934. }
  1935. }
  1936. temp = Resource->RetryPeriodOnFailure;
  1937. dwDefault = CLUSTER_RESOURCE_DEFAULT_RETRY_PERIOD_ON_FAILURE;
  1938. status = DmQueryDword( resourceKey,
  1939. CLUSREG_NAME_RES_RETRY_PERIOD_ON_FAILURE,
  1940. &Resource->RetryPeriodOnFailure,
  1941. &dwDefault );
  1942. // make sure that RetryPeriodOnFailure >= RestartPeriod
  1943. if (Resource->RetryPeriodOnFailure < Resource->RestartPeriod)
  1944. {
  1945. ClRtlLogPrint(LOG_NOISE,
  1946. "[FM] Specified RetryPeriodOnFailure value is less than RestartPeriod value - setting RetryPeriodOnFailure equal to RestartPeriod \n");
  1947. Resource->RetryPeriodOnFailure = Resource->RestartPeriod;
  1948. }
  1949. if( temp != Resource->RetryPeriodOnFailure)
  1950. notify = TRUE;
  1951. //
  1952. // Get the PendingTimeout value.
  1953. //
  1954. temp = Resource->PendingTimeout;
  1955. dwDefault = CLUSTER_RESOURCE_DEFAULT_PENDING_TIMEOUT;
  1956. status = DmQueryDword( resourceKey,
  1957. CLUSREG_NAME_RES_PENDING_TIMEOUT,
  1958. &Resource->PendingTimeout,
  1959. &dwDefault);
  1960. // Verify the PendingTimeout. Take any value.
  1961. if ( (status == NO_ERROR) &&
  1962. (temp != Resource->PendingTimeout) ) {
  1963. notify = TRUE;
  1964. }
  1965. //
  1966. // Get the current persistent state of the resource.
  1967. //
  1968. // Don't bother with change notifications... they should happen elsewhere.
  1969. //
  1970. status = DmQueryDword( resourceKey,
  1971. CLUSREG_NAME_RES_PERSISTENT_STATE,
  1972. &temp,
  1973. NULL );
  1974. //
  1975. // Save the current resource state.
  1976. //
  1977. if ( ( status == ERROR_FILE_NOT_FOUND ) ||
  1978. ( ( status == ERROR_SUCCESS ) && ( temp == CLUSTER_RESOURCE_DEFAULT_PERSISTENT_STATE ) ) ) {
  1979. switch ( Resource->Group->PersistentState ) {
  1980. case ClusterGroupOnline:
  1981. Resource->PersistentState = ClusterResourceOnline;
  1982. break;
  1983. case ClusterGroupOffline:
  1984. Resource->PersistentState = ClusterResourceOffline;
  1985. break;
  1986. default:
  1987. break;
  1988. }
  1989. } else if ( status != NO_ERROR ) {
  1990. ClRtlLogPrint(LOG_NOISE,
  1991. "[FM] Unable to read PersistentState for resource %1!ws!. Error %2!u!\n",
  1992. OmObjectId(Resource),
  1993. status);
  1994. return;
  1995. } else if ( temp ) {
  1996. Resource->PersistentState = ClusterResourceOnline;
  1997. } else {
  1998. Resource->PersistentState = ClusterResourceOffline;
  1999. }
  2000. #if 0 // Do this work when bringing the resource online!
  2001. //
  2002. // Determine the monitor to run this in. This is only updated from
  2003. // the node that owns the resource.
  2004. //
  2005. status = DmQueryDword( resourceKey,
  2006. CLUSREG_NAME_RES_SEPARATE_MONITOR,
  2007. &separateMonitor,
  2008. &zero );
  2009. //
  2010. // Only do work if the flag changes.
  2011. //
  2012. if ( (!separateMonitor &&
  2013. (Resource->Flags & RESOURCE_SEPARATE_MONITOR)) ||
  2014. (separateMonitor &&
  2015. ((Resource->Flags & RESOURCE_SEPARATE_MONITOR) == 0)) ) {
  2016. //
  2017. // If the resource is not offline or the quorum resource, then
  2018. // we'll have to wait until the cluster service restarts.
  2019. //
  2020. //
  2021. // The separate monitor flag has changed... tell ResMon to close
  2022. // the resource and then create it again.
  2023. //
  2024. if ( (Resource->State != ClusterResourceOffline) ||
  2025. Resource->QuorumResource ) {
  2026. ClRtlLogPrint(LOG_NOISE,
  2027. "[FM] Can't change separate monitor flag on the fly... we'll pick this up on next cluster start\n",
  2028. OmObjectId(Resource) );
  2029. // Now fall through to post the other changes.
  2030. } else {
  2031. if ( FmpPostNotification( (RM_NOTIFY_KEY)Resource,
  2032. RmRestartResource,
  2033. Resource->PersistentState ) ) {
  2034. return;
  2035. } else {
  2036. ClRtlLogPrint(LOG_UNUSUAL,
  2037. "[FM] Failed to post notification to restart resource '%1!ws!'.\n",
  2038. OmObjectId(Resource) );
  2039. }
  2040. }
  2041. }
  2042. #endif
  2043. if ( !FmpShutdown &&
  2044. notify ) {
  2045. //
  2046. // Comments from sunitas: Tell the resource monitor about the
  2047. // changes but do this from the worker thread. Originally, this
  2048. // used to be a post notification to the FmpRmWorkerThread
  2049. // which posts resmon notifications to clussvc.
  2050. //
  2051. OmReferenceObject(Resource);
  2052. FmpPostWorkItem(FM_EVENT_INTERNAL_RESOURCE_CHANGE_PARAMS,
  2053. Resource,
  2054. 0);
  2055. }
  2056. #if 0 // The post notification above handles the event notification
  2057. if ( !FmpShutdown &&
  2058. notify ) {
  2059. ClusterEvent( CLUSTER_EVENT_RESOURCE_PROPERTY_CHANGE, Resource );
  2060. }
  2061. #endif
  2062. return;
  2063. } // FmpResourceChangeCallback
  2064. DWORD
  2065. FmpChangeResourceMonitor(
  2066. IN PFM_RESOURCE Resource,
  2067. IN DWORD SeparateMonitor
  2068. )
  2069. /*++
  2070. Routine Description:
  2071. This routine switches the resource from one resource monitor to another.
  2072. Arguments:
  2073. Resource - pointer to the resource that was modified.
  2074. SeparateMonitor - flag to indicate whether to run in a separate monitor;
  2075. Return Value:
  2076. ERROR_SUCCESS if successful.
  2077. A Win32 error code on failure.
  2078. Notes:
  2079. The resource should be offline.
  2080. --*/
  2081. {
  2082. DWORD status = ERROR_SUCCESS;
  2083. DWORD separateMonitor;
  2084. DWORD zero = 0;
  2085. if ( Resource->RegistryKey == NULL ) {
  2086. return(ERROR_INVALID_STATE);
  2087. }
  2088. if ( (Resource->State != ClusterResourceOffline) &&
  2089. (Resource->State != ClusterResourceFailed) ) {
  2090. return(ERROR_INVALID_STATE);
  2091. }
  2092. //
  2093. // Determine the monitor to run this in. This is only updated from
  2094. // the node that owns the resource.
  2095. //
  2096. if ( (!SeparateMonitor &&
  2097. (Resource->Flags & RESOURCE_SEPARATE_MONITOR)) ||
  2098. (SeparateMonitor &&
  2099. ((Resource->Flags & RESOURCE_SEPARATE_MONITOR) == 0)) ) {
  2100. //
  2101. // The separate monitor flag has changed... tell ResMon to close
  2102. // the resource and then create it again.
  2103. //
  2104. ClRtlLogPrint(LOG_NOISE,
  2105. "[FM] Changing Separate Resource Monitor state\n");
  2106. status = FmpRmCloseResource( Resource );
  2107. if ( status == ERROR_SUCCESS ) {
  2108. if ( Resource->Flags & RESOURCE_SEPARATE_MONITOR ) {
  2109. Resource->Flags &= ~RESOURCE_SEPARATE_MONITOR;
  2110. } else {
  2111. Resource->Flags |= RESOURCE_SEPARATE_MONITOR;
  2112. }
  2113. status = FmpRmCreateResource( Resource );
  2114. if ( status != ERROR_SUCCESS ) {
  2115. ClRtlLogPrint(LOG_UNUSUAL,
  2116. "[FM] Separate resource monitor changed for '%1!ws!', but failed to re-open the resource, error %2!u!.\n",
  2117. OmObjectId(Resource),
  2118. status );
  2119. }
  2120. } else {
  2121. ClRtlLogPrint(LOG_UNUSUAL,
  2122. "[FM] Separate resource monitor changed for '%1!ws!', but failed to close the resource, error %2!u!.\n",
  2123. OmObjectId(Resource),
  2124. status );
  2125. return(status);
  2126. }
  2127. }
  2128. return(status);
  2129. } // FmpChangeResourceMonitor