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.

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