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.

3192 lines
86 KiB

  1. /*++
  2. Copyright (c) 1996-2000 Microsoft Corporation
  3. Module Name:
  4. clusapi.c
  5. Abstract:
  6. Public interfaces for managing clusters.
  7. Author:
  8. John Vert (jvert) 15-Jan-1996
  9. Revision History:
  10. --*/
  11. #include "clusapip.h"
  12. //
  13. // Bug 645590: ERROR_CLUSTER_OLD_VERSION must be removed from winerror.h
  14. // since it was not part of the (localized) WinXP RTM.
  15. //
  16. #ifndef ERROR_CLUSTER_OLD_VERSION
  17. #define ERROR_CLUSTER_OLD_VERSION 5904
  18. #endif // ERROR_CLUSTER_OLD_VERSION
  19. //
  20. // Local function prototype
  21. //
  22. HCLUSTER
  23. WINAPI
  24. OpenClusterAuthInfo(
  25. IN LPCWSTR lpszClusterName,
  26. IN unsigned long AuthnLevel
  27. );
  28. static DWORD
  29. GetOldClusterVersionInformation(
  30. IN HCLUSTER hCluster,
  31. IN OUT LPCLUSTERVERSIONINFO pClusterInfo
  32. );
  33. static
  34. DWORD
  35. GetNodeServiceState(
  36. IN LPCWSTR lpszNodeName,
  37. OUT DWORD * pdwClusterState
  38. );
  39. DWORD
  40. CopyCptFileToClusDirp(
  41. IN LPCWSTR lpszPathName
  42. );
  43. DWORD
  44. UnloadClusterHivep(
  45. VOID
  46. );
  47. //
  48. // ClusApi as of Jan/26/2000 has a race
  49. //
  50. // The usage of binding and context handles in PCLUSTER and
  51. // other structures is not synchronized with reconnect.
  52. //
  53. // Reconnect frees the handles and stuffes new ones in,
  54. // while other threads maybe using those handles.
  55. //
  56. // Trying to change as fewer lines as possible, the fix is implemented
  57. // that delays freeing binding and context handles for at least 40 seconds,
  58. // after the deletion was requested.
  59. //
  60. // We put a context or binding handle in a queue when the deletion is requested.
  61. // Periodically queues are cleaned up of handles that are more than 40 seconds old.
  62. //
  63. #define STALE_RPC_HANDLE_THRESHOLD 40
  64. RPC_STATUS
  65. FreeRpcBindingOrContext(
  66. IN PCLUSTER Cluster,
  67. IN void ** RpcHandlePtr,
  68. IN BOOL IsBinding)
  69. /*++
  70. Routine Description:
  71. Pushes an rpc handle to a tail of the queue
  72. Arguments:
  73. Cluster - pointer to a cluster structure
  74. RpcHandlePtr - rpc binding or context handle
  75. IsBinding - TRUE if RPC_BINDING_HANDLE is passed and FALSE if the context handle is passed
  76. Return Value:
  77. RPC_STATUS
  78. --*/
  79. {
  80. PCTX_HANDLE CtxHandle;
  81. PLIST_ENTRY ListHead = IsBinding ?
  82. &Cluster->FreedBindingList : &Cluster->FreedContextList;
  83. RPC_STATUS status;
  84. if (*RpcHandlePtr == NULL) {
  85. // If we tried more than one candidate,
  86. // some of the context handles can be NULL.
  87. // Don't need to free anything in this case
  88. return RPC_S_OK;
  89. }
  90. CtxHandle = LocalAlloc(LMEM_ZEROINIT, sizeof(CLUSTER));
  91. if (CtxHandle == NULL) {
  92. //
  93. // We ran out of memory.
  94. // Option #1. Leak the handle, but fix the race
  95. // Option #2. Free the handle and don't fix the race
  96. //
  97. // I vote for #2
  98. //
  99. if (IsBinding) {
  100. status = RpcBindingFree(RpcHandlePtr);
  101. } else {
  102. status = RpcSmDestroyClientContext(RpcHandlePtr);
  103. }
  104. } else {
  105. GetSystemTimeAsFileTime((LPFILETIME)&CtxHandle->TimeStamp);
  106. CtxHandle->TimeStamp += STALE_RPC_HANDLE_THRESHOLD * (ULONGLONG)10000000;
  107. CtxHandle->RpcHandle = *RpcHandlePtr;
  108. InsertTailList(ListHead, &CtxHandle->HandleList);
  109. ++Cluster->FreedRpcHandleListLen;
  110. status = RPC_S_OK;
  111. }
  112. return status;
  113. }
  114. VOID
  115. FreeObsoleteRpcHandlesEx(
  116. IN PCLUSTER Cluster,
  117. IN BOOL Cleanup,
  118. IN BOOL IsBinding
  119. )
  120. /*++
  121. Routine Description:
  122. runs down a queue and cleans stale rpc handles
  123. Arguments:
  124. Cluster - pointer to a cluster structure
  125. Cleanup - if TRUE all handles are freed regardless of the time stamp
  126. IsBinding - TRUE if we need to clean binding or context handle queue
  127. --*/
  128. {
  129. ULONGLONG CurrentTime;
  130. PLIST_ENTRY ListHead = IsBinding ?
  131. &Cluster->FreedBindingList : &Cluster->FreedContextList;
  132. EnterCriticalSection(&Cluster->Lock);
  133. GetSystemTimeAsFileTime((LPFILETIME)&CurrentTime);
  134. while (!IsListEmpty(ListHead))
  135. {
  136. PCTX_HANDLE Handle =
  137. CONTAINING_RECORD(
  138. ListHead->Flink,
  139. CTX_HANDLE,
  140. HandleList);
  141. if (!Cleanup && Handle->TimeStamp > CurrentTime) {
  142. // Not time yet
  143. break;
  144. }
  145. --Cluster->FreedRpcHandleListLen;
  146. if (IsBinding) {
  147. RpcBindingFree(&Handle->RpcHandle);
  148. } else {
  149. RpcSmDestroyClientContext(&Handle->RpcHandle);
  150. }
  151. RemoveHeadList(ListHead);
  152. LocalFree(Handle);
  153. }
  154. LeaveCriticalSection(&Cluster->Lock);
  155. }
  156. static DWORD
  157. GetOldClusterVersionInformation(
  158. IN HCLUSTER hCluster,
  159. IN OUT LPCLUSTERVERSIONINFO pClusterInfo
  160. )
  161. /*++
  162. Routine Description:
  163. Fixes up the cluster version information for downlevel clusters by looking at
  164. all of the nodes and returning the completed version information if all nodes
  165. are up. If a node is down and no up level nodes are found then we cannot say
  166. what version of Cluster that we have.
  167. Arguments:
  168. hCluster - Supplies a handle to the cluster
  169. pClusterInfo - returns the cluster version information structure.
  170. Return Value:
  171. If the function succeeds, the return value is ERROR_SUCCESS.
  172. If the function fails, the return value is an error value.
  173. --*/
  174. {
  175. DWORD dwError = ERROR_SUCCESS;
  176. DWORD dwType;
  177. HCLUSENUM hEnum = 0;
  178. WCHAR NameBuf[50];
  179. DWORD NameLen, i;
  180. HNODE Node;
  181. CLUSTER_NODE_STATE NodeState;
  182. HCLUSTER hClusNode;
  183. PCLUSTER pClus;
  184. WORD Major;
  185. WORD Minor;
  186. WORD Build;
  187. LPWSTR VendorId = NULL;
  188. LPWSTR CsdVersion = NULL;
  189. PCLUSTER_OPERATIONAL_VERSION_INFO pClusterOpVerInfo = NULL;
  190. BOOL bNodeDown = FALSE;
  191. BOOL bFoundSp4OrHigherNode = FALSE;
  192. hEnum = ClusterOpenEnum(hCluster, CLUSTER_ENUM_NODE);
  193. if (hEnum == NULL) {
  194. dwError = GetLastError();
  195. fprintf(stderr, "ClusterOpenEnum failed %d\n",dwError);
  196. goto FnExit;
  197. }
  198. for (i=0; ; i++) {
  199. dwError = ERROR_SUCCESS;
  200. NameLen = sizeof(NameBuf)/sizeof(WCHAR);
  201. dwError = ClusterEnum(hEnum, i, &dwType, NameBuf, &NameLen);
  202. if (dwError == ERROR_NO_MORE_ITEMS) {
  203. dwError = ERROR_SUCCESS;
  204. break;
  205. } else if (dwError != ERROR_SUCCESS) {
  206. fprintf(stderr, "ClusterEnum %d returned error %d\n",i,dwError);
  207. goto FnExit;
  208. }
  209. if (dwType != CLUSTER_ENUM_NODE) {
  210. printf("Invalid Type %d returned from ClusterEnum\n", dwType);
  211. goto FnExit;
  212. }
  213. hClusNode = OpenCluster(NameBuf);
  214. if (hClusNode == NULL) {
  215. bNodeDown = TRUE;
  216. dwError = GetLastError();
  217. fprintf(stderr, "OpenCluster %ws failed %d\n", NameBuf, dwError);
  218. continue;
  219. }
  220. pClus = GET_CLUSTER(hClusNode);
  221. WRAP(dwError,
  222. (ApiGetClusterVersion2(pClus->RpcBinding,
  223. &Major,
  224. &Minor,
  225. &Build,
  226. &VendorId,
  227. &CsdVersion,
  228. &pClusterOpVerInfo)),
  229. pClus);
  230. if (!CloseCluster(hClusNode)) {
  231. fprintf(stderr, "CloseCluster %ws failed %d\n", NameBuf, GetLastError());
  232. }
  233. if (dwError == RPC_S_PROCNUM_OUT_OF_RANGE) {
  234. dwError = ERROR_SUCCESS;
  235. continue;
  236. }
  237. else if (dwError != ERROR_SUCCESS) {
  238. fprintf(stderr, "ApiGetClusterVersion2 failed %d\n",dwError);
  239. bNodeDown = TRUE;
  240. continue;
  241. }
  242. else {
  243. pClusterInfo->MajorVersion = Major;
  244. pClusterInfo->MinorVersion = Minor;
  245. pClusterInfo->BuildNumber = Build;
  246. lstrcpynW(pClusterInfo->szVendorId, VendorId, 64);
  247. MIDL_user_free(VendorId);
  248. if (CsdVersion != NULL) {
  249. lstrcpynW(pClusterInfo->szCSDVersion, CsdVersion, 64);
  250. MIDL_user_free(CsdVersion);
  251. }
  252. else {
  253. pClusterInfo->szCSDVersion[0] = '\0';
  254. }
  255. pClusterInfo->dwClusterHighestVersion = pClusterOpVerInfo->dwClusterHighestVersion;
  256. pClusterInfo->dwClusterLowestVersion = pClusterOpVerInfo->dwClusterLowestVersion;
  257. pClusterInfo->dwFlags = pClusterOpVerInfo->dwFlags;
  258. bFoundSp4OrHigherNode = TRUE;
  259. break;
  260. }
  261. }
  262. // did not find a node higher than NT4Sp3
  263. if (!bFoundSp4OrHigherNode) {
  264. // no nodes were down, we can assume all nodes are NT4Sp3
  265. if (!bNodeDown) {
  266. pClusterInfo->dwClusterHighestVersion = pClusterInfo->dwClusterLowestVersion = MAKELONG(NT4_MAJOR_VERSION,pClusterInfo->BuildNumber);
  267. pClusterInfo->dwFlags = 0;
  268. }
  269. else { // at least one node was unreachable... punt and return unknown version...
  270. pClusterInfo->dwClusterHighestVersion = pClusterInfo->dwClusterLowestVersion = CLUSTER_VERSION_UNKNOWN;
  271. pClusterInfo->dwFlags = 0;
  272. }
  273. }
  274. FnExit:
  275. if (hEnum) ClusterCloseEnum(hEnum);
  276. return dwError;
  277. }
  278. //
  279. // General Cluster Management Routines.
  280. //
  281. DWORD
  282. WINAPI
  283. GetNodeClusterState(
  284. IN LPCWSTR lpszNodeName,
  285. OUT DWORD * pdwClusterState
  286. )
  287. /*++
  288. Routine Description:
  289. Finds out if this node is clustered.
  290. Arguments:
  291. lpszNodeName - The Name of the Node. If NULL, the local node is queried.
  292. pdwClusterState - A pointer to a DWORD where the cluster state
  293. for this node is returned. This is one of the enumerated types
  294. NODE_CLUSTER_STATE.
  295. Return Value:
  296. ERROR_SUCCESS if successful
  297. Win32 error code otherwise.
  298. --*/
  299. {
  300. DWORD dwStatus = ERROR_SUCCESS;
  301. eClusterInstallState eState = eClusterInstallStateUnknown;
  302. *pdwClusterState = ClusterStateNotInstalled;
  303. // Get the cluster install state from the registry.
  304. dwStatus = ClRtlGetClusterInstallState( lpszNodeName, &eState );
  305. if ( dwStatus != ERROR_SUCCESS )
  306. {
  307. goto FnExit;
  308. }
  309. // Translate the registry key setting into the external state value.
  310. switch ( eState )
  311. {
  312. case eClusterInstallStateUnknown:
  313. *pdwClusterState = ClusterStateNotInstalled;
  314. dwStatus = GetNodeServiceState( lpszNodeName, pdwClusterState );
  315. // If the service is not installed, map the error to success.
  316. if ( dwStatus == ERROR_SERVICE_DOES_NOT_EXIST )
  317. {
  318. dwStatus = ERROR_SUCCESS;
  319. *pdwClusterState = ClusterStateNotInstalled;
  320. }
  321. break;
  322. case eClusterInstallStateFilesCopied:
  323. *pdwClusterState = ClusterStateNotConfigured;
  324. break;
  325. case eClusterInstallStateConfigured:
  326. case eClusterInstallStateUpgraded:
  327. *pdwClusterState = ClusterStateNotRunning;
  328. dwStatus = GetNodeServiceState( lpszNodeName, pdwClusterState );
  329. break;
  330. default:
  331. *pdwClusterState = ClusterStateNotInstalled;
  332. break;
  333. } // switch: eState
  334. FnExit:
  335. return(dwStatus);
  336. } //*** GetNodeClusterState()
  337. static
  338. DWORD
  339. GetNodeServiceState(
  340. IN LPCWSTR lpszNodeName,
  341. OUT DWORD * pdwClusterState
  342. )
  343. /*++
  344. Routine Description:
  345. Finds out if the cluster service is installed on the specified node
  346. and whether it is running or not.
  347. Arguments:
  348. lpszNodeName - The name of the node. If NULL, the local node is queried.
  349. pdwClusterState - A pointer to a DWORD where the cluster state
  350. for this node is returned. This is one of the enumerated types
  351. NODE_CLUSTER_STATE.
  352. Return Value:
  353. ERROR_SUCCESS if successful
  354. Win32 error code otherwise.
  355. --*/
  356. {
  357. SC_HANDLE hScManager = NULL;
  358. SC_HANDLE hClusSvc = NULL;
  359. DWORD dwStatus = ERROR_SUCCESS;
  360. WCHAR szClusterServiceName[] = CLUSTER_SERVICE_NAME;
  361. SERVICE_STATUS ServiceStatus;
  362. // Open the Service Control Manager.
  363. hScManager = OpenSCManagerW( lpszNodeName, NULL, GENERIC_READ );
  364. if ( hScManager == NULL )
  365. {
  366. dwStatus = GetLastError();
  367. goto FnExit;
  368. }
  369. // Open the Cluster service.
  370. hClusSvc = OpenServiceW( hScManager, szClusterServiceName, GENERIC_READ );
  371. if ( hClusSvc == NULL )
  372. {
  373. dwStatus = GetLastError();
  374. goto FnExit;
  375. }
  376. // Assume that the service is not running.
  377. *pdwClusterState = ClusterStateNotRunning;
  378. if ( ! QueryServiceStatus( hClusSvc, &ServiceStatus ) )
  379. {
  380. dwStatus = GetLastError();
  381. goto FnExit;
  382. }
  383. // If succeeded in opening the handle to the service
  384. // we assume that the service is installed.
  385. if ( ServiceStatus.dwCurrentState == SERVICE_RUNNING )
  386. {
  387. *pdwClusterState = ClusterStateRunning;
  388. }
  389. else
  390. {
  391. HCLUSTER hCluster = NULL;
  392. hCluster = OpenCluster( lpszNodeName );
  393. if ( hCluster != NULL )
  394. {
  395. *pdwClusterState = ClusterStateRunning;
  396. CloseCluster( hCluster );
  397. }
  398. }
  399. FnExit:
  400. if ( hScManager )
  401. {
  402. CloseServiceHandle( hScManager );
  403. }
  404. if ( hClusSvc )
  405. {
  406. CloseServiceHandle( hClusSvc );
  407. }
  408. return(dwStatus);
  409. } //*** GetNodeServiceState()
  410. HCLUSTER
  411. WINAPI
  412. OpenCluster(
  413. IN LPCWSTR lpszClusterName
  414. )
  415. /*++
  416. Routine Description:
  417. Initiates a communication session with the specified cluster.
  418. Arguments:
  419. lpszClusterName - Supplies the name of the cluster to be opened.
  420. Return Value:
  421. non-NULL - returns an open handle to the specified cluster.
  422. NULL - The operation failed. Extended error status is available
  423. using GetLastError()
  424. --*/
  425. {
  426. return (OpenClusterAuthInfo(lpszClusterName, RPC_C_AUTHN_LEVEL_CONNECT));
  427. }
  428. HCLUSTER
  429. WINAPI
  430. OpenClusterAuthInfo(
  431. IN LPCWSTR lpszClusterName,
  432. IN unsigned long AuthnLevel
  433. )
  434. /*++
  435. Routine Description:
  436. Initiates a communication session with the specified cluster.
  437. Arguments:
  438. lpszClusterName - Supplies the name of the cluster to be opened.
  439. AuthnLevel - Level of authentication to be performed on remote procedure call.
  440. Return Value:
  441. non-NULL - returns an open handle to the specified cluster.
  442. NULL - The operation failed. Extended error status is available
  443. using GetLastError()
  444. --*/
  445. {
  446. PCLUSTER Cluster;
  447. BOOL Success;
  448. DWORD Status;
  449. WCHAR *Binding = NULL;
  450. DWORD MaxLen;
  451. Cluster = LocalAlloc(LMEM_ZEROINIT, sizeof(CLUSTER));
  452. if (Cluster == NULL) {
  453. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  454. return(NULL);
  455. }
  456. Cluster->Signature = CLUS_SIGNATURE;
  457. Cluster->ReferenceCount = 1;
  458. InitializeListHead(&Cluster->ResourceList);
  459. InitializeListHead(&Cluster->GroupList);
  460. InitializeListHead(&Cluster->KeyList);
  461. InitializeListHead(&Cluster->NodeList);
  462. InitializeListHead(&Cluster->NotifyList);
  463. InitializeListHead(&Cluster->SessionList);
  464. InitializeListHead(&Cluster->NetworkList);
  465. InitializeListHead(&Cluster->NetInterfaceList);
  466. Cluster->NotifyThread = NULL;
  467. //
  468. // Initialize the critsec. Catch low memory conditions and return error to caller.
  469. //
  470. try
  471. {
  472. InitializeCriticalSection(&Cluster->Lock);
  473. } except ( EXCEPTION_EXECUTE_HANDLER )
  474. {
  475. SetLastError( GetExceptionCode() );
  476. LocalFree( Cluster );
  477. return( NULL );
  478. }
  479. InitializeListHead(&Cluster->FreedBindingList);
  480. InitializeListHead(&Cluster->FreedContextList);
  481. //
  482. // Determine which node we should connect to. If someone has
  483. // passed in NULL, we know we can connect to the cluster service
  484. // over LPC. Otherwise, use RPC.
  485. //
  486. if ((lpszClusterName == NULL) ||
  487. (lpszClusterName[0] == '\0')) {
  488. Status = RpcStringBindingComposeW(L"b97db8b2-4c63-11cf-bff6-08002be23f2f",
  489. L"ncalrpc",
  490. NULL,
  491. NULL, // dynamic endpoint
  492. NULL,
  493. &Binding);
  494. if (Status != RPC_S_OK) {
  495. goto error_exit;
  496. }
  497. Cluster->Flags = CLUS_LOCALCONNECT;
  498. Status = RpcBindingFromStringBindingW(Binding, &Cluster->RpcBinding);
  499. RpcStringFreeW(&Binding);
  500. if (Status != RPC_S_OK) {
  501. goto error_exit;
  502. }
  503. } else {
  504. //
  505. // Try to connect directly to the cluster.
  506. //
  507. Status = RpcStringBindingComposeW(L"b97db8b2-4c63-11cf-bff6-08002be23f2f",
  508. L"ncadg_ip_udp",
  509. (LPWSTR)lpszClusterName,
  510. NULL,
  511. NULL,
  512. &Binding);
  513. if (Status != RPC_S_OK) {
  514. goto error_exit;
  515. }
  516. Status = RpcBindingFromStringBindingW(Binding, &Cluster->RpcBinding);
  517. RpcStringFreeW(&Binding);
  518. if (Status != RPC_S_OK) {
  519. goto error_exit;
  520. }
  521. //
  522. // Resolve the binding handle endpoint
  523. //
  524. Status = RpcEpResolveBinding(Cluster->RpcBinding,
  525. clusapi_v2_0_c_ifspec);
  526. if (Status != RPC_S_OK) {
  527. goto error_exit;
  528. }
  529. Cluster->Flags = 0;
  530. }
  531. //
  532. // no SPN required for NTLM. This will need to change if we decide to use
  533. // kerb in the future
  534. //
  535. Cluster->AuthnLevel=AuthnLevel;
  536. Status = RpcBindingSetAuthInfoW(Cluster->RpcBinding,
  537. NULL,
  538. AuthnLevel,
  539. RPC_C_AUTHN_WINNT,
  540. NULL,
  541. RPC_C_AUTHZ_NAME);
  542. if (Status != RPC_S_OK) {
  543. goto error_exit;
  544. }
  545. //
  546. // Get the cluster and node name from the remote machine.
  547. // This is also a good check to make sure there is really
  548. // an RPC server on the other end of this binding.
  549. //
  550. WRAP(Status,
  551. (ApiGetClusterName(Cluster->RpcBinding,
  552. &Cluster->ClusterName,
  553. &Cluster->NodeName)),
  554. Cluster);
  555. if (Status != RPC_S_OK) {
  556. goto error_exit;
  557. }
  558. WRAP_NULL(Cluster->hCluster,
  559. (ApiOpenCluster(Cluster->RpcBinding, &Status)),
  560. &Status,
  561. Cluster);
  562. if (Cluster->hCluster == NULL) {
  563. goto error_exit;
  564. }
  565. Status = GetReconnectCandidates(Cluster);
  566. if (Status != ERROR_SUCCESS) {
  567. goto error_exit;
  568. }
  569. return((HCLUSTER)Cluster);
  570. error_exit:
  571. if (Cluster != NULL) {
  572. if (Cluster->RpcBinding != NULL) {
  573. RpcBindingFree(&Cluster->RpcBinding);
  574. }
  575. if (Cluster->ClusterName != NULL) {
  576. MIDL_user_free(Cluster->ClusterName);
  577. }
  578. if (Cluster->NodeName != NULL) {
  579. MIDL_user_free(Cluster->NodeName);
  580. }
  581. DeleteCriticalSection(&Cluster->Lock);
  582. LocalFree(Cluster);
  583. }
  584. SetLastError(Status);
  585. return(NULL);
  586. }
  587. BOOL
  588. WINAPI
  589. CloseCluster(
  590. IN HCLUSTER hCluster
  591. )
  592. /*++
  593. Routine Description:
  594. Closes a cluster handle returned from OpenCluster
  595. Arguments:
  596. hCluster - Supplies the cluster handle
  597. Return Value:
  598. TRUE - The operation was successful.
  599. FALSE - The operation failed. Extended error status is available
  600. using GetLastError()
  601. --*/
  602. {
  603. PCLUSTER Cluster;
  604. PCRITICAL_SECTION Lock;
  605. Cluster = GET_CLUSTER(hCluster);
  606. EnterCriticalSection(&Cluster->Lock);
  607. Cluster->ReferenceCount--;
  608. if ( Cluster->ReferenceCount == 0 ) {
  609. Cluster->Flags |= CLUS_DELETED;
  610. //
  611. // Free up any notifications posted on this cluster handle.
  612. //
  613. RundownNotifyEvents(&Cluster->NotifyList, Cluster->ClusterName);
  614. if (Cluster->Flags & CLUS_DEAD) {
  615. RpcSmDestroyClientContext(&Cluster->hCluster);
  616. } else {
  617. ApiCloseCluster(&Cluster->hCluster);
  618. }
  619. LeaveCriticalSection(&Cluster->Lock);
  620. //
  621. // If this was the only thing keeping the cluster structure
  622. // around, clean it up now.
  623. //
  624. CleanupCluster(Cluster);
  625. } else {
  626. LeaveCriticalSection(&Cluster->Lock);
  627. }
  628. return(TRUE);
  629. }
  630. VOID
  631. CleanupCluster(
  632. IN PCLUSTER Cluster
  633. )
  634. /*++
  635. Routine Description:
  636. Frees any system resources associated with a cluster.
  637. N.B. This routine will delete the Cluster->Lock critical
  638. section. Any thread waiting on this lock will hang.
  639. Arguments:
  640. Cluster - Supplies the cluster structure to be cleaned up
  641. Return Value:
  642. None.
  643. --*/
  644. {
  645. EnterCriticalSection(&Cluster->Lock);
  646. if (IS_CLUSTER_FREE(Cluster)) {
  647. RpcBindingFree(&Cluster->RpcBinding);
  648. Cluster->RpcBinding = NULL;
  649. FreeObsoleteRpcHandles(Cluster, TRUE);
  650. LeaveCriticalSection(&Cluster->Lock);
  651. DeleteCriticalSection(&Cluster->Lock);
  652. MIDL_user_free(Cluster->ClusterName);
  653. MIDL_user_free(Cluster->NodeName);
  654. FreeReconnectCandidates(Cluster);
  655. LocalFree(Cluster);
  656. } else {
  657. LeaveCriticalSection(&Cluster->Lock);
  658. }
  659. }
  660. DWORD
  661. WINAPI
  662. SetClusterName(
  663. IN HCLUSTER hCluster,
  664. IN LPCWSTR lpszNewClusterName
  665. )
  666. /*++
  667. Routine Description:
  668. Sets the cluster name.
  669. Arguments:
  670. hCluster - Supplies the cluster handle.
  671. lpszNewClusterName - Supplies a pointer to the new cluster name.
  672. Return Value:
  673. ERROR_SUCCESS if the cluster information was returned successfully.
  674. If an error occurs, the Win32 error code is returned.
  675. Notes:
  676. This API requires TBD privilege.
  677. --*/
  678. {
  679. LPWSTR NewName;
  680. DWORD NameLength;
  681. DWORD Status;
  682. PCLUSTER Cluster;
  683. Cluster = GET_CLUSTER(hCluster);
  684. NameLength = (lstrlenW(lpszNewClusterName)+1)*sizeof(WCHAR);
  685. NewName = MIDL_user_allocate(NameLength);
  686. if (NewName == NULL) {
  687. return(ERROR_NOT_ENOUGH_MEMORY);
  688. }
  689. CopyMemory(NewName, lpszNewClusterName, NameLength);
  690. WRAP(Status, (ApiSetClusterName(Cluster->RpcBinding, lpszNewClusterName)), Cluster);
  691. if ((Status == ERROR_SUCCESS) || (Status == ERROR_RESOURCE_PROPERTIES_STORED)) {
  692. EnterCriticalSection(&Cluster->Lock);
  693. MIDL_user_free(Cluster->ClusterName);
  694. Cluster->ClusterName = NewName;
  695. LeaveCriticalSection(&Cluster->Lock);
  696. } else {
  697. MIDL_user_free(NewName);
  698. }
  699. return(Status);
  700. }
  701. DWORD
  702. WINAPI
  703. GetClusterInformation(
  704. IN HCLUSTER hCluster,
  705. OUT LPWSTR lpszClusterName,
  706. IN OUT LPDWORD lpcchClusterName,
  707. OUT OPTIONAL LPCLUSTERVERSIONINFO lpClusterInfo
  708. )
  709. /*++
  710. Routine Description:
  711. Gets the cluster's name
  712. Arguments:
  713. hCluster - Supplies a handle to the cluster
  714. lpszClusterName - Points to a buffer that receives the name of the cluster,
  715. including the terminating null character.
  716. lpcchClusterName - Points to a variable that specifies the size, in characters,
  717. of the buffer pointed to by the lpszClusterName parameter. This size
  718. should include the terminating null character. When the function
  719. returns, the variable pointed to by lpcchClusterName contains the number of
  720. characters stored in the buffer. The count returned does not include
  721. the terminating null character.
  722. lpClusterInfo - Optionally returns the cluster version information structure.
  723. Return Value:
  724. If the function succeeds, the return value is ERROR_SUCCESS.
  725. If the function fails, the return value is an error value.
  726. --*/
  727. {
  728. PCLUSTER Cluster;
  729. DWORD Length;
  730. LPWSTR pszClusterName=NULL;
  731. LPWSTR pszNodeName = NULL;
  732. DWORD Status = ERROR_SUCCESS;
  733. DWORD Status2;
  734. PCLUSTER_OPERATIONAL_VERSION_INFO pClusterOpVerInfo = NULL;
  735. Cluster = GET_CLUSTER(hCluster);
  736. if ( Cluster == NULL ) {
  737. return(ERROR_INVALID_HANDLE);
  738. }
  739. WRAP(Status,
  740. (ApiGetClusterName(Cluster->RpcBinding,
  741. &pszClusterName,
  742. &pszNodeName)),
  743. Cluster);
  744. if (Status != ERROR_SUCCESS)
  745. goto FnExit;
  746. MylstrcpynW(lpszClusterName, pszClusterName, *lpcchClusterName);
  747. Length = lstrlenW(pszClusterName);
  748. if (Length >= *lpcchClusterName) {
  749. if (lpszClusterName == NULL) {
  750. Status = ERROR_SUCCESS;
  751. } else {
  752. Status = ERROR_MORE_DATA;
  753. }
  754. }
  755. *lpcchClusterName = Length;
  756. if (lpClusterInfo != NULL)
  757. {
  758. WORD Major;
  759. WORD Minor;
  760. WORD Build;
  761. LPWSTR VendorId = NULL;
  762. LPWSTR CsdVersion = NULL;
  763. BOOL bOldServer = FALSE;
  764. if (lpClusterInfo->dwVersionInfoSize < sizeof(CLUSTERVERSIONINFO_NT4))
  765. {
  766. return(ERROR_INVALID_PARAMETER);
  767. }
  768. WRAP(Status2,
  769. (ApiGetClusterVersion2(Cluster->RpcBinding,
  770. &Major,
  771. &Minor,
  772. &Build,
  773. &VendorId,
  774. &CsdVersion,
  775. &pClusterOpVerInfo)),
  776. Cluster);
  777. //if this was an older server, call the older call
  778. if (Status2 == RPC_S_PROCNUM_OUT_OF_RANGE)
  779. {
  780. bOldServer = TRUE;
  781. WRAP(Status2,
  782. (ApiGetClusterVersion(Cluster->RpcBinding,
  783. &Major,
  784. &Minor,
  785. &Build,
  786. &VendorId,
  787. &CsdVersion)),
  788. Cluster);
  789. }
  790. if (Status2 != ERROR_SUCCESS)
  791. {
  792. Status = Status2;
  793. goto FnExit;
  794. }
  795. lpClusterInfo->MajorVersion = Major;
  796. lpClusterInfo->MinorVersion = Minor;
  797. lpClusterInfo->BuildNumber = Build;
  798. lstrcpynW(lpClusterInfo->szVendorId, VendorId, 64);
  799. MIDL_user_free(VendorId);
  800. if (CsdVersion != NULL)
  801. {
  802. lstrcpynW(lpClusterInfo->szCSDVersion, CsdVersion, 64);
  803. MIDL_user_free(CsdVersion);
  804. }
  805. else
  806. {
  807. lpClusterInfo->szCSDVersion[0] = '\0';
  808. }
  809. //support older clients of clusapi.dll
  810. // if the structure passed in is smaller than the new version
  811. // the cluster version info is not returned
  812. if (lpClusterInfo->dwVersionInfoSize < sizeof(CLUSTERVERSIONINFO))
  813. {
  814. goto FnExit;
  815. }
  816. //nt 4 client, return without the operational cluster version
  817. //or on an nt 5 client connected to an older version
  818. if (bOldServer)
  819. {
  820. Status = GetOldClusterVersionInformation(hCluster, lpClusterInfo);
  821. }
  822. else
  823. {
  824. lpClusterInfo->dwClusterHighestVersion = pClusterOpVerInfo->dwClusterHighestVersion;
  825. lpClusterInfo->dwClusterLowestVersion = pClusterOpVerInfo->dwClusterLowestVersion;
  826. lpClusterInfo->dwFlags = pClusterOpVerInfo->dwFlags;
  827. }
  828. }
  829. FnExit:
  830. if (pszClusterName)
  831. MIDL_user_free(pszClusterName);
  832. if (pszNodeName)
  833. MIDL_user_free(pszNodeName);
  834. if (pClusterOpVerInfo)
  835. MIDL_user_free(pClusterOpVerInfo);
  836. return(Status);
  837. }
  838. DWORD
  839. WINAPI
  840. GetClusterQuorumResource(
  841. IN HCLUSTER hCluster,
  842. OUT LPWSTR lpszResourceName,
  843. IN OUT LPDWORD lpcchResourceName,
  844. OUT LPWSTR lpszDeviceName,
  845. IN OUT LPDWORD lpcchDeviceName,
  846. OUT LPDWORD lpdwMaxQuorumLogSize
  847. )
  848. /*++
  849. Routine Description:
  850. Gets the current cluster quorum resource
  851. Arguments:
  852. hCluster - Supplies the cluster handle.
  853. lpszResourceName - Points to a buffer that receives the name of
  854. the cluster quorum resource, including the terminating NULL character.
  855. lpcchResourceName - Points to a variable that specifies the size,
  856. in characters, of the buffer pointed to by the lpszResourceName
  857. parameter. This size should include the terminating null character.
  858. When the function returns, the variable pointed to by lpcchResourceName
  859. contains the number of characters stored in the buffer. The count
  860. returned does not include the terminating null character.
  861. lpszDeviceName - Points to a buffer that receives the path name for
  862. the cluster quorum log file.
  863. lpcchDeviceName - Points to a variable that specifies the size,
  864. in characters, of the buffer pointed to by the lpszDeviceName
  865. parameter. This size should include the terminating null character.
  866. When the function returns, the variable pointed to by lpcchResourceName
  867. contains the number of characters stored in the buffer. The count
  868. returned does not include the terminating null character.
  869. pdwMaxQuorumLogSize - Points to a variable that receives the current maximum
  870. size of the quorum log files.
  871. Return Value:
  872. ERROR_SUCCESS if the cluster information was returned successfully.
  873. If an error occurs, the Win32 error code is returned.
  874. Notes:
  875. This API requires TBD privilege.
  876. --*/
  877. {
  878. PCLUSTER Cluster;
  879. LPWSTR ResourceName = NULL;
  880. LPWSTR DeviceName = NULL;
  881. DWORD Status;
  882. DWORD Length;
  883. Cluster = GET_CLUSTER(hCluster);
  884. WRAP(Status,
  885. (ApiGetQuorumResource(Cluster->RpcBinding,
  886. &ResourceName, &DeviceName,
  887. lpdwMaxQuorumLogSize)),
  888. Cluster);
  889. if (Status != ERROR_SUCCESS) {
  890. return(Status);
  891. }
  892. MylstrcpynW(lpszResourceName, ResourceName, *lpcchResourceName);
  893. Length = lstrlenW(ResourceName);
  894. if (Length >= *lpcchResourceName) {
  895. if (lpszResourceName == NULL) {
  896. Status = ERROR_SUCCESS;
  897. } else {
  898. Status = ERROR_MORE_DATA;
  899. }
  900. }
  901. *lpcchResourceName = Length;
  902. MIDL_user_free(ResourceName);
  903. MylstrcpynW(lpszDeviceName, DeviceName, *lpcchDeviceName);
  904. Length = lstrlenW(DeviceName);
  905. if (Length >= *lpcchDeviceName) {
  906. if (Status == ERROR_SUCCESS) {
  907. if (lpszDeviceName == NULL) {
  908. Status = ERROR_SUCCESS;
  909. } else {
  910. Status = ERROR_MORE_DATA;
  911. }
  912. }
  913. }
  914. *lpcchDeviceName = Length;
  915. MIDL_user_free(DeviceName);
  916. return(Status);
  917. }
  918. DWORD
  919. WINAPI
  920. SetClusterQuorumResource(
  921. IN HRESOURCE hResource,
  922. IN LPCWSTR lpszDeviceName,
  923. IN DWORD dwMaxQuorumLogSize
  924. )
  925. /*++
  926. Routine Description:
  927. Sets the cluster quorum resource.
  928. Arguments:
  929. hResource - Supplies the new clster quorum resource.
  930. lpszDeviceName - The path where the permanent cluster files like the
  931. quorum and check point files will be maintained. If the drive
  932. letter is specified, it will be validated for the given resource. If
  933. no drive letter is specified in the path, the first drive letter will
  934. be chosen. If NULL, the first drive letter will be chosen and the default
  935. path used.
  936. dwMaxQuorumLogSize - The maximum size of the quorum logs before they are
  937. reset by checkpointing. If 0, the default is used.
  938. Return Value:
  939. ERROR_SUCCESS if the cluster resource was set successfully
  940. If an error occurs, the Win32 error code is returned.
  941. Notes:
  942. This API requires TBD privilege.
  943. --*/
  944. {
  945. DWORD Status;
  946. PCRESOURCE Resource = (PCRESOURCE)hResource;
  947. WCHAR szNull = L'\0';
  948. //
  949. // Chittur Subbaraman (chitturs) - 1/6/99
  950. //
  951. // Substitute a pointer to a NULL character for a NULL pointer.
  952. // This is necessary since RPC refuses to accept a NULL pointer.
  953. //
  954. if( !ARGUMENT_PRESENT( lpszDeviceName ) )
  955. {
  956. lpszDeviceName = &szNull;
  957. }
  958. WRAP(Status,
  959. (ApiSetQuorumResource(Resource->hResource, lpszDeviceName, dwMaxQuorumLogSize)),
  960. Resource->Cluster);
  961. return(Status);
  962. }
  963. DWORD
  964. WINAPI
  965. SetClusterNetworkPriorityOrder(
  966. IN HCLUSTER hCluster,
  967. IN DWORD NetworkCount,
  968. IN HNETWORK NetworkList[]
  969. )
  970. /*++
  971. Routine Description:
  972. Sets the priority order for the set of cluster networks used for
  973. internal (node-to-node) cluster communication. Internal communication
  974. is always carried on the highest priority network that is available
  975. between two nodes.
  976. Arguments:
  977. hCluster - Supplies the cluster handle.
  978. NetworkCount - The number of items in NetworkList.
  979. NetworkList - A prioritized array of network object handles.
  980. The first handle in the array has the highest priority.
  981. All of the networks that are eligible to carry internal
  982. communication must be represented in the list. No networks
  983. that are ineligible to carry internal communication may
  984. appear in the list.
  985. Return Value:
  986. If the function succeeds, the return value is ERROR_SUCCESS.
  987. If the function fails, the return value is an error value.
  988. --*/
  989. {
  990. PCLUSTER Cluster;
  991. DWORD i,j;
  992. LPWSTR *IdArray;
  993. DWORD Status;
  994. PCNETWORK Network;
  995. Cluster = GET_CLUSTER(hCluster);
  996. //
  997. // First, iterate through all the networks and obtain their IDs.
  998. //
  999. IdArray = LocalAlloc(LMEM_ZEROINIT, NetworkCount*sizeof(LPWSTR));
  1000. if (IdArray == NULL) {
  1001. return( ERROR_NOT_ENOUGH_MEMORY );
  1002. }
  1003. for (i=0; i<NetworkCount; i++) {
  1004. Network = (PCNETWORK)NetworkList[i];
  1005. //
  1006. // Make sure this isn't a handle to a network from a different
  1007. // cluster
  1008. //
  1009. if (Network->Cluster != Cluster) {
  1010. Status = ERROR_INVALID_PARAMETER;
  1011. goto error_exit;
  1012. }
  1013. WRAP(Status,
  1014. (ApiGetNetworkId(Network->hNetwork,
  1015. &IdArray[i])),
  1016. Cluster);
  1017. if (Status != ERROR_SUCCESS) {
  1018. goto error_exit;
  1019. }
  1020. //
  1021. // Make sure there are no duplicates
  1022. //
  1023. for (j=0; j<i; j++) {
  1024. if (lstrcmpiW(IdArray[j],IdArray[i]) == 0) {
  1025. //
  1026. // A duplicate node is in the list
  1027. //
  1028. Status = ERROR_INVALID_PARAMETER;
  1029. goto error_exit;
  1030. }
  1031. }
  1032. }
  1033. WRAP(Status,
  1034. (ApiSetNetworkPriorityOrder(Cluster->RpcBinding,
  1035. NetworkCount,
  1036. IdArray)),
  1037. Cluster);
  1038. error_exit:
  1039. for (i=0; i<NetworkCount; i++) {
  1040. if (IdArray[i] != NULL) {
  1041. MIDL_user_free(IdArray[i]);
  1042. }
  1043. }
  1044. LocalFree(IdArray);
  1045. return(Status);
  1046. }
  1047. HCHANGE
  1048. WINAPI
  1049. FindFirstClusterChangeNotification(
  1050. IN HCLUSTER hCluster,
  1051. IN DWORD fdwFilter,
  1052. IN DWORD Reserved,
  1053. IN HANDLE hEvent
  1054. )
  1055. /*++
  1056. Routine Description:
  1057. Creates a change notification object that is associated with a
  1058. specified cluster. The object permits the notification of
  1059. cluster changes based on a specified filter.
  1060. Arguments:
  1061. hCluster - Supplies a handle of a cluster.
  1062. fdwFilter - A set of bit flags that specifies the conditions that will
  1063. cause the notification to occur. Currently defined conditions
  1064. include:
  1065. CLUSTER_CHANGE_NODE_STATE
  1066. CLUSTER_CHANGE_NODE_ADDED
  1067. CLUSTER_CHANGE_NODE_DELETED
  1068. CLUSTER_CHANGE_RESOURCE_STATE
  1069. CLUSTER_CHANGE_RESOURCE_ADDED
  1070. CLUSTER_CHANGE_RESOURCE_DELETED
  1071. CLUSTER_CHANGE_RESOURCE_TYPE_ADDED
  1072. CLUSTER_CHANGE_RESOURCE_TYPE_DELETED
  1073. CLUSTER_CHANGE_QUORUM_STATE
  1074. Reserved - Reserved, must be zero
  1075. hEvent - Supplies a handle to a manual-reset event object that will enter
  1076. the signaled state when one of the conditions specified in the
  1077. filter occurs.
  1078. Return Value:
  1079. If the function is successful, the return value is a handle of the
  1080. change notification object.
  1081. If the function fails, the return value is NULL. To get extended error
  1082. information, call GetLastError.
  1083. Remarks:
  1084. Applications may wait for notifications by using WaitForSingleObject or
  1085. WaitForMultipleObjects on the specified event handle. When a cluster
  1086. change occurs which passes the condition filter, the wait is satisfied.
  1087. After the wait has been satisfied, applications may respond to the
  1088. notification and continue monitoring the cluster by calling
  1089. FindNextClusterChangeNotification and the appropriate wait function.
  1090. When the notification handle is no longer needed, it can be closed
  1091. by calling FindCloseClusterChangeNotification.
  1092. --*/
  1093. {
  1094. if (Reserved != 0) {
  1095. SetLastError(ERROR_INVALID_PARAMETER);
  1096. return(NULL);
  1097. }
  1098. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  1099. return(NULL);
  1100. }
  1101. DWORD
  1102. WINAPI
  1103. FindNextClusterChangeNotification(
  1104. IN HCHANGE hChange,
  1105. OUT OPTIONAL LPWSTR lpszName,
  1106. IN OUT LPDWORD lpcchName
  1107. )
  1108. /*++
  1109. Routine Description:
  1110. Retrieves information associated with a cluster change. Optionally returns
  1111. the name of the cluster object that the notification is associated with.
  1112. Resets the event object associated with the specified change notification
  1113. handle. The event object will be signaled the next time a change
  1114. meeting the change object condition filter occurs.
  1115. Arguments:
  1116. hChange - Supplies a handle of a cluster change notification object.
  1117. lpszName - if present, Returns the name of the cluster object that the
  1118. notification is associated with.
  1119. lpcchName - Only used if lpszName != NULL. Supplies a pointer to the length
  1120. in characters of the buffer pointed to by lpszName. Returns the
  1121. number of characters (not including the terminating NULL) written
  1122. to the buffer.
  1123. Return Value:
  1124. Returns the bit flag that indicates what the cluster event is.
  1125. If the function fails, it returns 0. To get extended error information,
  1126. call GetLastError.
  1127. Remarks:
  1128. The function retrieves the next change notifications and
  1129. resets the associated event object.
  1130. If the associated event object is not signalled, this function
  1131. blocks until a notification event occurs.
  1132. --*/
  1133. {
  1134. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  1135. return(FALSE);
  1136. }
  1137. BOOL
  1138. WINAPI
  1139. FindCloseClusterChangeNotification(
  1140. IN HCHANGE hChange
  1141. )
  1142. /*++
  1143. Routine Description:
  1144. Closes a handle of a change notification object.
  1145. Arguments:
  1146. hChange - Supplies a handle of a cluster change notification object
  1147. to close.
  1148. Return Value:
  1149. If the function is successful, the return value is TRUE.
  1150. If the function fails, the return value is FALSE. To get extended error
  1151. information, call GetLastError.
  1152. Remarks:
  1153. --*/
  1154. {
  1155. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  1156. return(FALSE);
  1157. }
  1158. HCLUSENUM
  1159. WINAPI
  1160. ClusterOpenEnum(
  1161. IN HCLUSTER hCluster,
  1162. IN DWORD dwType
  1163. )
  1164. /*++
  1165. Routine Description:
  1166. Initiates an enumeration of the existing cluster objects.
  1167. Arguments:
  1168. hCluster - Supplies a handle to a cluster.
  1169. dwType - Supplies a bitmask of the type of objects to be
  1170. enumerated. Currently defined types include
  1171. CLUSTER_ENUM_NODE - Cluster nodes
  1172. CLUSTER_ENUM_RESTYPE - Cluster resource types
  1173. CLUSTER_ENUM_RESOURCE - Cluster resources (except group resources)
  1174. CLUSTER_ENUM_GROUPS - Cluster group resources
  1175. CLUSTER_ENUM_NETWORK - Cluster networks
  1176. CLUSTER_ENUM_NETWORK_INTERFACE - Cluster network interfaces
  1177. CLUSTER_ENUM_INTERNAL_NETWORK - Networks used for internal
  1178. communication in highest to
  1179. lowest priority order. May not
  1180. be used in conjunction with any
  1181. other types.
  1182. Return Value:
  1183. If successful, returns a handle suitable for use with ClusterEnum
  1184. If unsuccessful, returns NULL and GetLastError() returns a more
  1185. specific error code.
  1186. --*/
  1187. {
  1188. PCLUSTER Cluster;
  1189. PENUM_LIST Enum = NULL;
  1190. DWORD Status;
  1191. if (dwType & CLUSTER_ENUM_INTERNAL_NETWORK) {
  1192. if ((dwType & ~CLUSTER_ENUM_INTERNAL_NETWORK) != 0) {
  1193. SetLastError(ERROR_INVALID_PARAMETER);
  1194. return(NULL);
  1195. }
  1196. }
  1197. else {
  1198. if ((dwType & CLUSTER_ENUM_ALL) == 0) {
  1199. SetLastError(ERROR_INVALID_PARAMETER);
  1200. return(NULL);
  1201. }
  1202. if ((dwType & ~CLUSTER_ENUM_ALL) != 0) {
  1203. SetLastError(ERROR_INVALID_PARAMETER);
  1204. return(NULL);
  1205. }
  1206. }
  1207. Cluster = (PCLUSTER)hCluster;
  1208. WRAP(Status,
  1209. (ApiCreateEnum(Cluster->RpcBinding,
  1210. dwType,
  1211. &Enum)),
  1212. Cluster);
  1213. if (Status != ERROR_SUCCESS) {
  1214. SetLastError(Status);
  1215. return(NULL);
  1216. }
  1217. return((HCLUSENUM)Enum);
  1218. }
  1219. DWORD
  1220. WINAPI
  1221. ClusterGetEnumCount(
  1222. IN HCLUSENUM hEnum
  1223. )
  1224. /*++
  1225. Routine Description:
  1226. Gets the number of items contained the the enumerator's collection.
  1227. Arguments:
  1228. hEnum - a handle to an enumerator returned by ClusterOpenEnum.
  1229. Return Value:
  1230. The number of items (possibly zero) in the enumerator's collection.
  1231. --*/
  1232. {
  1233. PENUM_LIST Enum = (PENUM_LIST)hEnum;
  1234. return Enum->EntryCount;
  1235. }
  1236. DWORD
  1237. WINAPI
  1238. ClusterEnum(
  1239. IN HCLUSENUM hEnum,
  1240. IN DWORD dwIndex,
  1241. OUT LPDWORD lpdwType,
  1242. OUT LPWSTR lpszName,
  1243. IN OUT LPDWORD lpcchName
  1244. )
  1245. /*++
  1246. Routine Description:
  1247. Returns the next enumerable object.
  1248. Arguments:
  1249. hEnum - Supplies a handle to an open cluster enumeration returned by
  1250. ClusterOpenEnum
  1251. dwIndex - Supplies the index to enumerate. This parameter should be
  1252. zero for the first call to the ClusterEnum function and then
  1253. incremented for subsequent calls.
  1254. dwType - Returns the type of object.
  1255. lpszName - Points to a buffer that receives the name of the object,
  1256. including the terminating null character.
  1257. lpcchName - Points to a variable that specifies the size, in characters,
  1258. of the buffer pointed to by the lpszName parameter. This size
  1259. should include the terminating null character. When the function returns,
  1260. the variable pointed to by lpcchName contains the number of
  1261. characters stored in the buffer. The count returned does not include
  1262. the terminating null character.
  1263. Return Value:
  1264. If the function succeeds, the return value is ERROR_SUCCESS.
  1265. If the function fails, the return value is an error value.
  1266. --*/
  1267. {
  1268. DWORD Status;
  1269. DWORD NameLen;
  1270. PENUM_LIST Enum = (PENUM_LIST)hEnum;
  1271. if (dwIndex >= Enum->EntryCount) {
  1272. return(ERROR_NO_MORE_ITEMS);
  1273. }
  1274. NameLen = lstrlenW(Enum->Entry[dwIndex].Name);
  1275. MylstrcpynW(lpszName, Enum->Entry[dwIndex].Name, *lpcchName);
  1276. if (*lpcchName < (NameLen + 1)) {
  1277. if (lpszName == NULL) {
  1278. Status = ERROR_SUCCESS;
  1279. } else {
  1280. Status = ERROR_MORE_DATA;
  1281. }
  1282. } else {
  1283. Status = ERROR_SUCCESS;
  1284. }
  1285. *lpdwType = Enum->Entry[dwIndex].Type;
  1286. *lpcchName = NameLen;
  1287. return(Status);
  1288. }
  1289. DWORD
  1290. WINAPI
  1291. ClusterCloseEnum(
  1292. IN HCLUSENUM hEnum
  1293. )
  1294. /*++
  1295. Routine Description:
  1296. Closes an open enumeration.
  1297. Arguments:
  1298. hEnum - Supplies a handle to the enumeration to be closed.
  1299. Return Value:
  1300. If the function succeeds, the return value is ERROR_SUCCESS.
  1301. If the function fails, the return value is an error value.
  1302. --*/
  1303. {
  1304. DWORD i;
  1305. PENUM_LIST Enum = (PENUM_LIST)hEnum;
  1306. //
  1307. // Walk through enumeration freeing all the names
  1308. //
  1309. for (i=0; i<Enum->EntryCount; i++) {
  1310. MIDL_user_free(Enum->Entry[i].Name);
  1311. Enum->Entry[i].Name = NULL;
  1312. }
  1313. //
  1314. // Set this to a bogus value so people who are reusing closed stuff
  1315. // will be unpleasantly surprised
  1316. //
  1317. Enum->EntryCount = (ULONG)-1;
  1318. MIDL_user_free(Enum);
  1319. return(ERROR_SUCCESS);
  1320. }
  1321. HNODEENUM
  1322. WINAPI
  1323. ClusterNodeOpenEnum(
  1324. IN HNODE hNode,
  1325. IN DWORD dwType
  1326. )
  1327. /*++
  1328. Routine Description:
  1329. Initiates an enumeration of the existing cluster node objects.
  1330. Arguments:
  1331. hNode - Supplies a handle to the specific node.
  1332. dwType - Supplies a bitmask of the type of properties to be
  1333. enumerated. Currently defined types include
  1334. CLUSTER_NODE_ENUM_NETINTERFACES - all net interfaces associated
  1335. with this node
  1336. Return Value:
  1337. If successful, returns a handle suitable for use with ClusterNodeEnum
  1338. If unsuccessful, returns NULL and GetLastError() returns a more
  1339. specific error code.
  1340. --*/
  1341. {
  1342. PCNODE Node = (PCNODE)hNode;
  1343. PENUM_LIST Enum = NULL;
  1344. DWORD errorStatus;
  1345. if ((dwType & CLUSTER_NODE_ENUM_ALL) == 0) {
  1346. SetLastError(ERROR_INVALID_PARAMETER);
  1347. return(NULL);
  1348. }
  1349. if ((dwType & ~CLUSTER_NODE_ENUM_ALL) != 0) {
  1350. SetLastError(ERROR_INVALID_PARAMETER);
  1351. return(NULL);
  1352. }
  1353. WRAP(errorStatus,
  1354. (ApiCreateNodeEnum(Node->hNode, dwType, &Enum)),
  1355. Node->Cluster);
  1356. if (errorStatus != ERROR_SUCCESS) {
  1357. SetLastError(errorStatus);
  1358. return(NULL);
  1359. }
  1360. return((HNODEENUM)Enum);
  1361. }
  1362. DWORD
  1363. WINAPI
  1364. ClusterNodeGetEnumCount(
  1365. IN HNODEENUM hNodeEnum
  1366. )
  1367. /*++
  1368. Routine Description:
  1369. Gets the number of items contained the the enumerator's collection.
  1370. Arguments:
  1371. hEnum - a handle to an enumerator returned by ClusterNodeOpenEnum.
  1372. Return Value:
  1373. The number of items (possibly zero) in the enumerator's collection.
  1374. --*/
  1375. {
  1376. PENUM_LIST Enum = (PENUM_LIST)hNodeEnum;
  1377. return Enum->EntryCount;
  1378. }
  1379. DWORD
  1380. WINAPI
  1381. ClusterNodeEnum(
  1382. IN HNODEENUM hNodeEnum,
  1383. IN DWORD dwIndex,
  1384. OUT LPDWORD lpdwType,
  1385. OUT LPWSTR lpszName,
  1386. IN OUT LPDWORD lpcchName
  1387. )
  1388. /*++
  1389. Routine Description:
  1390. Returns the next enumerable object.
  1391. Arguments:
  1392. hNodeEnum - Supplies a handle to an open cluster node enumeration
  1393. returned by ClusterNodeOpenEnum
  1394. dwIndex - Supplies the index to enumerate. This parameter should be
  1395. zero for the first call to the ClusterEnum function and then
  1396. incremented for subsequent calls.
  1397. lpdwType - Points to a DWORD that receives the type of the object
  1398. being enumerated
  1399. lpszName - Points to a buffer that receives the name of the object,
  1400. including the terminating null character.
  1401. lpcchName - Points to a variable that specifies the size, in characters,
  1402. of the buffer pointed to by the lpszName parameter. This size
  1403. should include the terminating null character. When the function
  1404. returns, the variable pointed to by lpcchName contains the
  1405. number of characters stored in the buffer. The count returned
  1406. does not include the terminating null character.
  1407. Return Value:
  1408. If the function succeeds, the return value is ERROR_SUCCESS.
  1409. If the function fails, the return value is an error value.
  1410. --*/
  1411. {
  1412. DWORD Status;
  1413. DWORD NameLen;
  1414. PENUM_LIST Enum = (PENUM_LIST)hNodeEnum;
  1415. if (dwIndex >= Enum->EntryCount) {
  1416. return(ERROR_NO_MORE_ITEMS);
  1417. }
  1418. NameLen = lstrlenW(Enum->Entry[dwIndex].Name);
  1419. MylstrcpynW(lpszName, Enum->Entry[dwIndex].Name, *lpcchName);
  1420. if (*lpcchName < (NameLen + 1)) {
  1421. if (lpszName == NULL) {
  1422. Status = ERROR_SUCCESS;
  1423. } else {
  1424. Status = ERROR_MORE_DATA;
  1425. }
  1426. } else {
  1427. Status = ERROR_SUCCESS;
  1428. }
  1429. *lpdwType = Enum->Entry[dwIndex].Type;
  1430. *lpcchName = NameLen;
  1431. return(Status);
  1432. }
  1433. DWORD
  1434. WINAPI
  1435. ClusterNodeCloseEnum(
  1436. IN HNODEENUM hNodeEnum
  1437. )
  1438. /*++
  1439. Routine Description:
  1440. Closes an open enumeration for a node.
  1441. Arguments:
  1442. hNodeEnum - Supplies a handle to the enumeration to be closed.
  1443. Return Value:
  1444. If the function succeeds, the return value is ERROR_SUCCESS.
  1445. If the function fails, the return value is an error value.
  1446. --*/
  1447. {
  1448. DWORD i;
  1449. PENUM_LIST Enum = (PENUM_LIST)hNodeEnum;
  1450. //
  1451. // Walk through enumeration freeing all the names
  1452. //
  1453. for (i=0; i<Enum->EntryCount; i++) {
  1454. MIDL_user_free(Enum->Entry[i].Name);
  1455. }
  1456. MIDL_user_free(Enum);
  1457. return(ERROR_SUCCESS);
  1458. }
  1459. HGROUPENUM
  1460. WINAPI
  1461. ClusterGroupOpenEnum(
  1462. IN HGROUP hGroup,
  1463. IN DWORD dwType
  1464. )
  1465. /*++
  1466. Routine Description:
  1467. Initiates an enumeration of the existing cluster group objects.
  1468. Arguments:
  1469. hGroup - Supplies a handle to the specific group.
  1470. dwType - Supplies a bitmask of the type of properties to be
  1471. enumerated. Currently defined types include
  1472. CLUSTER_GROUP_ENUM_CONTAINS - All resources contained in the specified
  1473. group
  1474. CLUSTER_GROUP_ENUM_NODES - All nodes in the specified group's preferred
  1475. owner list.
  1476. Return Value:
  1477. If successful, returns a handle suitable for use with ClusterGroupEnum
  1478. If unsuccessful, returns NULL and GetLastError() returns a more
  1479. specific error code.
  1480. --*/
  1481. {
  1482. PCGROUP Group = (PCGROUP)hGroup;
  1483. PENUM_LIST Enum = NULL;
  1484. DWORD errorStatus;
  1485. if ((dwType & CLUSTER_GROUP_ENUM_ALL) == 0) {
  1486. SetLastError(ERROR_INVALID_PARAMETER);
  1487. return(NULL);
  1488. }
  1489. if ((dwType & ~CLUSTER_GROUP_ENUM_ALL) != 0) {
  1490. SetLastError(ERROR_INVALID_PARAMETER);
  1491. return(NULL);
  1492. }
  1493. WRAP(errorStatus,
  1494. (ApiCreateGroupResourceEnum(Group->hGroup,
  1495. dwType,
  1496. &Enum)),
  1497. Group->Cluster);
  1498. if (errorStatus != ERROR_SUCCESS) {
  1499. SetLastError(errorStatus);
  1500. return(NULL);
  1501. }
  1502. return((HGROUPENUM)Enum);
  1503. }
  1504. DWORD
  1505. WINAPI
  1506. ClusterGroupGetEnumCount(
  1507. IN HGROUPENUM hGroupEnum
  1508. )
  1509. /*++
  1510. Routine Description:
  1511. Gets the number of items contained the the enumerator's collection.
  1512. Arguments:
  1513. hEnum - a handle to an enumerator returned by ClusterGroupOpenEnum.
  1514. Return Value:
  1515. The number of items (possibly zero) in the enumerator's collection.
  1516. --*/
  1517. {
  1518. PENUM_LIST Enum = (PENUM_LIST)hGroupEnum;
  1519. return Enum->EntryCount;
  1520. }
  1521. DWORD
  1522. WINAPI
  1523. ClusterGroupEnum(
  1524. IN HGROUPENUM hGroupEnum,
  1525. IN DWORD dwIndex,
  1526. OUT LPDWORD lpdwType,
  1527. OUT LPWSTR lpszName,
  1528. IN OUT LPDWORD lpcchName
  1529. )
  1530. /*++
  1531. Routine Description:
  1532. Returns the next enumerable resource object.
  1533. Arguments:
  1534. hGroupEnum - Supplies a handle to an open cluster group enumeration
  1535. returned by ClusterGroupOpenEnum
  1536. dwIndex - Supplies the index to enumerate. This parameter should be
  1537. zero for the first call to the ClusterGroupEnum function and then
  1538. incremented for subsequent calls.
  1539. lpszName - Points to a buffer that receives the name of the object,
  1540. including the terminating null character.
  1541. lpcchName - Points to a variable that specifies the size, in characters,
  1542. of the buffer pointed to by the lpszName parameter. This size
  1543. should include the terminating null character. When the function
  1544. returns, the variable pointed to by lpcchName contains the
  1545. number of characters stored in the buffer. The count returned
  1546. does not include the terminating null character.
  1547. Return Value:
  1548. If the function succeeds, the return value is ERROR_SUCCESS.
  1549. If the function fails, the return value is an error value.
  1550. --*/
  1551. {
  1552. DWORD Status;
  1553. DWORD NameLen;
  1554. PENUM_LIST Enum = (PENUM_LIST)hGroupEnum;
  1555. if (dwIndex >= Enum->EntryCount) {
  1556. return(ERROR_NO_MORE_ITEMS);
  1557. }
  1558. NameLen = lstrlenW(Enum->Entry[dwIndex].Name);
  1559. MylstrcpynW(lpszName, Enum->Entry[dwIndex].Name, *lpcchName);
  1560. if (*lpcchName < (NameLen + 1)) {
  1561. if (lpszName == NULL) {
  1562. Status = ERROR_SUCCESS;
  1563. } else {
  1564. Status = ERROR_MORE_DATA;
  1565. }
  1566. } else {
  1567. Status = ERROR_SUCCESS;
  1568. }
  1569. *lpdwType = Enum->Entry[dwIndex].Type;
  1570. *lpcchName = NameLen;
  1571. return(Status);
  1572. }
  1573. DWORD
  1574. WINAPI
  1575. ClusterGroupCloseEnum(
  1576. IN HGROUPENUM hGroupEnum
  1577. )
  1578. /*++
  1579. Routine Description:
  1580. Closes an open enumeration for a group.
  1581. Arguments:
  1582. hGroupEnum - Supplies a handle to the enumeration to be closed.
  1583. Return Value:
  1584. If the function succeeds, the return value is ERROR_SUCCESS.
  1585. If the function fails, the return value is an error value.
  1586. --*/
  1587. {
  1588. DWORD i;
  1589. PENUM_LIST Enum = (PENUM_LIST)hGroupEnum;
  1590. //
  1591. // Walk through enumeration freeing all the names
  1592. //
  1593. for (i=0; i<Enum->EntryCount; i++) {
  1594. MIDL_user_free(Enum->Entry[i].Name);
  1595. }
  1596. MIDL_user_free(Enum);
  1597. return(ERROR_SUCCESS);
  1598. }
  1599. VOID
  1600. APIENTRY
  1601. MylstrcpynW(
  1602. LPWSTR lpString1,
  1603. LPCWSTR lpString2,
  1604. DWORD iMaxLength
  1605. )
  1606. {
  1607. LPWSTR src,dst;
  1608. src = (LPWSTR)lpString2;
  1609. dst = lpString1;
  1610. if ( iMaxLength ) {
  1611. while(iMaxLength && *src){
  1612. *dst++ = *src++;
  1613. iMaxLength--;
  1614. }
  1615. if ( iMaxLength ) {
  1616. *dst = L'\0';
  1617. } else {
  1618. dst--;
  1619. *dst = L'\0';
  1620. }
  1621. }
  1622. }
  1623. /****
  1624. @func DWORD | BackupClusterDatabase | Requests for backup
  1625. of the cluster database files and resource registry
  1626. checkpoint files to a specified directory path. This
  1627. directory path must preferably be visible to all nodes
  1628. in the cluster (such as a UNC path) or if it is not a UNC
  1629. path it should at least be visible to the node on which the
  1630. quorum resource is online.
  1631. @parm IN HCLUSTER | hCluster | Supplies a handle to
  1632. an open cluster.
  1633. @parm IN LPCWSTR | lpszPathName | Supplies the directory path
  1634. where the quorum log file and the checkpoint file must
  1635. be backed up. This path must be visible to the node on
  1636. which the quorum resource is online.
  1637. @comm This function requests for backup of the quorum log file
  1638. and the related checkpoint files for the cluster hive.
  1639. This API backs up all the registry checkpoint files that
  1640. resources have registered for replication. The API backend
  1641. is responsible for directing the call to the owner node of
  1642. the quorum resource and for synchronizing this operation
  1643. with state of the quorum resource. If successful, the
  1644. database files will be saved to the supplied directory path
  1645. with the same name as in the quorum disk. Note that in case
  1646. this API hits a cluster node while the quorum group is moving
  1647. to another node, it is possible that the API will fail with
  1648. an error code ERROR_HOST_NODE_NOT_RESOURCE_OWNER. In such a
  1649. case, the client has to call the API again.
  1650. @rdesc Returns a Win32 error code if the operation is
  1651. unsuccessful. ERROR_SUCCESS on success.
  1652. @xref <f RestoreClusterDatabase>
  1653. ****/
  1654. DWORD
  1655. WINAPI
  1656. BackupClusterDatabase(
  1657. IN HCLUSTER hCluster,
  1658. IN LPCWSTR lpszPathName
  1659. )
  1660. {
  1661. DWORD dwStatus;
  1662. PCLUSTER pCluster;
  1663. //
  1664. // Chittur Subbaraman (chitturs) - 10/20/98
  1665. //
  1666. pCluster = GET_CLUSTER(hCluster);
  1667. WRAP( dwStatus,
  1668. ( ApiBackupClusterDatabase( pCluster->RpcBinding, lpszPathName ) ),
  1669. pCluster );
  1670. return( dwStatus );
  1671. }
  1672. /****
  1673. @func DWORD | RestoreClusterDatabase | Restores the cluster
  1674. database from the supplied path to the quorum disk and
  1675. restarts the cluster service on the restoring node.
  1676. @parm IN LPCWSTR | lpszPathName | Supplies the path from where
  1677. the cluster database has to be retrieved
  1678. @parm IN BOOL | bForce | Should the restore operation be done
  1679. by force performing fixups silently ?
  1680. @parm IN BOOL | lpszQuorumDriveLetter | If the user has replaced
  1681. the quorum drive since the time of backup, specifies the
  1682. drive letter of the quorum device. This is an optional
  1683. parameter.
  1684. @comm This API can work under the following scenarios:
  1685. (1) No cluster nodes are active.
  1686. (2) One or more cluster nodes are active.
  1687. (3) Quorum disk replaced since the time the backup was made.
  1688. The replacement disk must have identical partition layout
  1689. to the quorum disk at the time the backup was made. However,
  1690. the new disk may have different drive letter(s) and/or
  1691. signature from the original quorum disk.
  1692. (4) User wants to get the cluster back to a previous state.
  1693. @rdesc Returns a Win32 error code if the operation is unsuccessful.
  1694. ERROR_SUCCESS on success.
  1695. @xref <f BackupClusterDatabase>
  1696. ****/
  1697. DWORD
  1698. WINAPI
  1699. RestoreClusterDatabase(
  1700. IN LPCWSTR lpszPathName,
  1701. IN BOOL bForce,
  1702. IN LPCWSTR lpszQuorumDriveLetter OPTIONAL
  1703. )
  1704. {
  1705. SC_HANDLE hService = NULL;
  1706. SC_HANDLE hSCManager = NULL;
  1707. DWORD dwStatus = ERROR_SUCCESS;
  1708. DWORD dwRetryTime = 120*1000; // wait 120 secs max for shutdown
  1709. DWORD dwRetryTick = 5000; // 5 sec at a time
  1710. SERVICE_STATUS serviceStatus;
  1711. BOOL bStopCommandGiven = FALSE;
  1712. DWORD dwLen;
  1713. HKEY hClusSvcKey = NULL;
  1714. DWORD dwExitCode;
  1715. //
  1716. // Chittur Subbaraman (chitturs) - 10/29/98
  1717. //
  1718. //
  1719. // Check the validity of parameters
  1720. //
  1721. if ( lpszQuorumDriveLetter != NULL )
  1722. {
  1723. dwLen = lstrlenW( lpszQuorumDriveLetter );
  1724. if ( ( dwLen != 2 ) ||
  1725. !iswalpha( lpszQuorumDriveLetter[0] ) ||
  1726. ( lpszQuorumDriveLetter[1] != L':' ) )
  1727. {
  1728. dwStatus = ERROR_INVALID_PARAMETER;
  1729. TIME_PRINT(("Quorum drive letter '%ws' is invalid\n",
  1730. lpszQuorumDriveLetter));
  1731. goto FnExit;
  1732. }
  1733. }
  1734. hSCManager = OpenSCManager( NULL, // assume local machine
  1735. NULL, // ServicesActive database
  1736. SC_MANAGER_ALL_ACCESS ); // all access
  1737. if ( hSCManager == NULL )
  1738. {
  1739. dwStatus = GetLastError();
  1740. TIME_PRINT(("RestoreDatabase: Cannot access service controller! Error: %u.\n",
  1741. dwStatus));
  1742. goto FnExit;
  1743. }
  1744. hService = OpenService( hSCManager,
  1745. "clussvc",
  1746. SERVICE_ALL_ACCESS );
  1747. if ( hService == NULL )
  1748. {
  1749. dwStatus = GetLastError();
  1750. CloseServiceHandle( hSCManager );
  1751. TIME_PRINT(("RestoreClusterDatabase: Cannot open cluster service. Error: %u.\n",
  1752. dwStatus));
  1753. goto FnExit;
  1754. }
  1755. CloseServiceHandle( hSCManager );
  1756. //
  1757. // Check whether the service is already in the SERVICE_STOPPED
  1758. // state.
  1759. //
  1760. if ( QueryServiceStatus( hService,
  1761. &serviceStatus ) )
  1762. {
  1763. if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
  1764. {
  1765. TIME_PRINT(("RestoreClusterDatabase: Cluster service is already in stopped state\n"));
  1766. goto bypass_stop_procedure;
  1767. }
  1768. }
  1769. //
  1770. // Now attempt to stop the cluster service
  1771. //
  1772. while ( TRUE )
  1773. {
  1774. dwStatus = ERROR_SUCCESS;
  1775. if ( bStopCommandGiven == TRUE )
  1776. {
  1777. if ( QueryServiceStatus( hService,
  1778. &serviceStatus ) )
  1779. {
  1780. if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
  1781. {
  1782. //
  1783. // Succeeded in stopping the service
  1784. //
  1785. TIME_PRINT(("RestoreClusterDatabase: Clussvc stopped successfully\n"));
  1786. break;
  1787. }
  1788. } else
  1789. {
  1790. dwStatus = GetLastError();
  1791. TIME_PRINT(("RestoreClusterDatabase: Error %d in querying clussvc status\n",
  1792. dwStatus));
  1793. }
  1794. } else
  1795. {
  1796. if ( ControlService( hService,
  1797. SERVICE_CONTROL_STOP,
  1798. &serviceStatus ) )
  1799. {
  1800. bStopCommandGiven = TRUE;
  1801. dwStatus = ERROR_SUCCESS;
  1802. } else
  1803. {
  1804. dwStatus = GetLastError();
  1805. TIME_PRINT(("RestoreClusterDatabase: Error %d in sending control to stop clussvc\n",
  1806. dwStatus));
  1807. }
  1808. }
  1809. if ( ( dwStatus == ERROR_EXCEPTION_IN_SERVICE ) ||
  1810. ( dwStatus == ERROR_PROCESS_ABORTED ) ||
  1811. ( dwStatus == ERROR_SERVICE_NOT_ACTIVE ) )
  1812. {
  1813. //
  1814. // The service is essentially in a terminated state
  1815. //
  1816. TIME_PRINT(("RestoreClusterDatabase: Clussvc in died/inactive state\n"));
  1817. dwStatus = ERROR_SUCCESS;
  1818. break;
  1819. }
  1820. if ( ( dwRetryTime -= dwRetryTick ) <= 0 )
  1821. {
  1822. //
  1823. // All tries to stop the service failed, exit from this
  1824. // function with an error code
  1825. //
  1826. TIME_PRINT(("RestoreClusterDatabase: Cluster service did not stop, giving up..."));
  1827. dwStatus = ERROR_TIMEOUT;
  1828. break;
  1829. }
  1830. TIME_PRINT(("RestoreClusterDatabase: Trying to stop cluster service\n"));
  1831. //
  1832. // Sleep for a while and retry stopping the service
  1833. //
  1834. Sleep( dwRetryTick );
  1835. } // while
  1836. if ( dwStatus != ERROR_SUCCESS )
  1837. {
  1838. goto FnExit;
  1839. }
  1840. bypass_stop_procedure:
  1841. //
  1842. // Open key to SYSTEM\CurrentControlSet\Services\ClusSvc\Parameters
  1843. //
  1844. if ( RegOpenKeyW( HKEY_LOCAL_MACHINE,
  1845. CLUSREG_KEYNAME_CLUSSVC_PARAMETERS,
  1846. &hClusSvcKey ) != ERROR_SUCCESS )
  1847. {
  1848. TIME_PRINT(("RestoreClusterDatabase: Unable to open clussvc parameters key\n"));
  1849. goto FnExit;
  1850. }
  1851. dwLen = lstrlenW ( lpszPathName );
  1852. //
  1853. // Set the RestoreDatabase value so that the cluster service
  1854. // will read it at startup time
  1855. //
  1856. if ( ( dwStatus = RegSetValueExW( hClusSvcKey,
  1857. CLUSREG_NAME_SVC_PARAM_RESTORE_DB,
  1858. 0,
  1859. REG_SZ,
  1860. (BYTE * const) lpszPathName,
  1861. ( dwLen + 1 ) * sizeof ( WCHAR ) ) ) != ERROR_SUCCESS )
  1862. {
  1863. TIME_PRINT(("RestoreClusterDatabase: Unable to set %ws value\n",
  1864. CLUSREG_NAME_SVC_PARAM_RESTORE_DB));
  1865. goto FnExit;
  1866. }
  1867. if ( bForce == TRUE )
  1868. {
  1869. //
  1870. // Since the user is forcing a database restore operation, set
  1871. // the ForceDatabaseRestore value and the NewQuorumDriveLetter
  1872. // value, if any
  1873. //
  1874. dwLen = 0;
  1875. if ( ( dwStatus = RegSetValueExW( hClusSvcKey,
  1876. CLUSREG_NAME_SVC_PARAM_FORCE_RESTORE_DB,
  1877. 0,
  1878. REG_DWORD,
  1879. (BYTE * const) &dwLen,
  1880. sizeof ( DWORD ) ) ) != ERROR_SUCCESS )
  1881. {
  1882. TIME_PRINT(("RestoreClusterDatabase: Unable to set %ws value\n",
  1883. CLUSREG_NAME_SVC_PARAM_FORCE_RESTORE_DB));
  1884. goto FnExit;
  1885. }
  1886. if ( lpszQuorumDriveLetter != NULL )
  1887. {
  1888. dwLen = lstrlenW( lpszQuorumDriveLetter );
  1889. if ( ( dwStatus = RegSetValueExW( hClusSvcKey,
  1890. CLUSREG_NAME_SVC_PARAM_QUORUM_DRIVE_LETTER,
  1891. 0,
  1892. REG_SZ,
  1893. (BYTE * const) lpszQuorumDriveLetter,
  1894. ( dwLen + 1 ) * sizeof ( WCHAR ) ) ) != ERROR_SUCCESS )
  1895. {
  1896. TIME_PRINT(("RestoreClusterDatabase: Unable to set %ws value\n",
  1897. CLUSREG_NAME_SVC_PARAM_QUORUM_DRIVE_LETTER));
  1898. goto FnExit;
  1899. }
  1900. }
  1901. }
  1902. //
  1903. // Copy the latest checkpoint file from the backup area to the
  1904. // cluster directory and rename it as CLUSDB
  1905. //
  1906. dwStatus = CopyCptFileToClusDirp ( lpszPathName );
  1907. if ( dwStatus != ERROR_SUCCESS )
  1908. {
  1909. TIME_PRINT(("RestoreClusterDatabase: Unable to copy checkpoint file to CLUSDB\n"
  1910. ));
  1911. goto FnExit;
  1912. }
  1913. //
  1914. // Sleep for some time before starting the service so that any UP nodes may cleanly finish
  1915. // their node down processing before the start of the service.
  1916. //
  1917. Sleep( 12 * 1000 );
  1918. //
  1919. // Now, start the cluster service
  1920. //
  1921. if ( !StartService( hService,
  1922. 0,
  1923. NULL ) )
  1924. {
  1925. dwStatus = GetLastError();
  1926. TIME_PRINT(("RestoreClusterDatabase: Unable to start cluster service\n"
  1927. ));
  1928. goto FnExit;
  1929. }
  1930. dwRetryTime = 5 * 60 * 1000;
  1931. dwRetryTick = 1 * 1000;
  1932. while ( TRUE )
  1933. {
  1934. if ( !QueryServiceStatus( hService,
  1935. &serviceStatus ) )
  1936. {
  1937. dwStatus = GetLastError();
  1938. TIME_PRINT(("RestoreClusterDatabase: Unable to get the status of cluster service to check liveness\n"
  1939. ));
  1940. goto FnExit;
  1941. }
  1942. if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
  1943. {
  1944. //
  1945. // The service terminated after our start up. Exit with
  1946. // an error code.
  1947. //
  1948. dwStatus = serviceStatus.dwServiceSpecificExitCode;
  1949. if ( dwStatus == ERROR_SUCCESS )
  1950. {
  1951. dwStatus = serviceStatus.dwWin32ExitCode;
  1952. }
  1953. TIME_PRINT(("RestoreClusterDatabase: Cluster service stopped after starting up\n"
  1954. ));
  1955. goto FnExit;
  1956. } else if ( serviceStatus.dwCurrentState == SERVICE_RUNNING )
  1957. {
  1958. //
  1959. // The service has fully started up and is running.
  1960. //
  1961. dwStatus = ERROR_SUCCESS;
  1962. TIME_PRINT(("RestoreClusterDatabase: Cluster service started successfully\n"
  1963. ));
  1964. break;
  1965. }
  1966. if ( ( dwRetryTime -= dwRetryTick ) <= 0 )
  1967. {
  1968. dwStatus = ERROR_TIMEOUT;
  1969. TIME_PRINT(("RestoreClusterDatabase: Cluster service has not started even after %d minutes, giving up monitoring...\n",
  1970. dwRetryTime/(60*1000)));
  1971. goto FnExit;
  1972. }
  1973. Sleep( dwRetryTick );
  1974. }
  1975. FnExit:
  1976. if ( hService != NULL )
  1977. {
  1978. CloseServiceHandle( hService );
  1979. }
  1980. if ( hClusSvcKey != NULL )
  1981. {
  1982. //
  1983. // Try to delete the values you set. You may fail in this step,
  1984. // beware !
  1985. //
  1986. RegDeleteValueW( hClusSvcKey,
  1987. CLUSREG_NAME_SVC_PARAM_RESTORE_DB );
  1988. if ( bForce == TRUE )
  1989. {
  1990. RegDeleteValueW( hClusSvcKey,
  1991. CLUSREG_NAME_SVC_PARAM_FORCE_RESTORE_DB );
  1992. if ( lpszQuorumDriveLetter != NULL )
  1993. {
  1994. RegDeleteValueW( hClusSvcKey,
  1995. CLUSREG_NAME_SVC_PARAM_QUORUM_DRIVE_LETTER );
  1996. }
  1997. }
  1998. RegCloseKey( hClusSvcKey );
  1999. }
  2000. return( dwStatus );
  2001. }
  2002. /****
  2003. @func DWORD | CopyCptFileToClusDirp | Copy the most recent checkpoint
  2004. file from the backup path to the cluster directory overwriting
  2005. the CLUSDB there.
  2006. @parm IN LPCWSTR | lpszPathName | Supplies the path from where
  2007. the checkpoint file has to be retrieved.
  2008. @rdesc Returns a Win32 error code if the operation is
  2009. unsuccessful. ERROR_SUCCESS on success.
  2010. @xref <f RestoreClusterDatabase>
  2011. ****/
  2012. DWORD
  2013. CopyCptFileToClusDirp(
  2014. IN LPCWSTR lpszPathName
  2015. )
  2016. {
  2017. HANDLE hFindFile = INVALID_HANDLE_VALUE;
  2018. WIN32_FIND_DATAW FindData;
  2019. DWORD dwStatus;
  2020. WCHAR szDestFileName[MAX_PATH];
  2021. LPWSTR szSourceFileName = NULL;
  2022. LPWSTR szSourcePathName = NULL;
  2023. DWORD dwLen;
  2024. WIN32_FILE_ATTRIBUTE_DATA FileAttributes;
  2025. LARGE_INTEGER liFileCreationTime;
  2026. LARGE_INTEGER liMaxFileCreationTime;
  2027. WCHAR szCheckpointFileName[MAX_PATH];
  2028. WCHAR szClusterDir[MAX_PATH];
  2029. //
  2030. // Chittur Subbaraman (chitturs) - 10/29/98
  2031. //
  2032. dwLen = lstrlenW ( lpszPathName );
  2033. //
  2034. // It is safer to use dynamic memory allocation for user-supplied
  2035. // path since we don't want to put restrictions on the user
  2036. // on the length of the path that can be supplied. However, as
  2037. // far as our own destination path is concerned, it is system-dependent
  2038. // and static memory allocation for that would suffice.
  2039. //
  2040. szSourcePathName = (LPWSTR) LocalAlloc ( LMEM_FIXED,
  2041. ( dwLen + 25 ) *
  2042. sizeof ( WCHAR ) );
  2043. if ( szSourcePathName == NULL )
  2044. {
  2045. dwStatus = GetLastError();
  2046. TIME_PRINT(("CopyCptFileToClusDirp: Error %d in allocating memory for %ws\n",
  2047. dwStatus,
  2048. lpszPathName));
  2049. goto FnExit;
  2050. }
  2051. lstrcpyW ( szSourcePathName, lpszPathName );
  2052. //
  2053. // If the client-supplied path is not already terminated with '\',
  2054. // then add it.
  2055. //
  2056. if ( szSourcePathName [dwLen-1] != L'\\' )
  2057. {
  2058. szSourcePathName [dwLen++] = L'\\';
  2059. szSourcePathName [dwLen] = L'\0';
  2060. }
  2061. lstrcatW ( szSourcePathName, L"CLUSBACKUP.DAT" );
  2062. //
  2063. // Try to find the CLUSBACKUP.DAT file in the directory
  2064. //
  2065. hFindFile = FindFirstFileW( szSourcePathName, &FindData );
  2066. //
  2067. // Reuse the source path name variable
  2068. //
  2069. szSourcePathName[dwLen] = L'\0';
  2070. if ( hFindFile == INVALID_HANDLE_VALUE )
  2071. {
  2072. dwStatus = GetLastError();
  2073. if ( dwStatus != ERROR_FILE_NOT_FOUND )
  2074. {
  2075. TIME_PRINT(("CopyCptFileToClusDirp: Path %ws unavailable, Error = %d\n",
  2076. szSourcePathName,
  2077. dwStatus));
  2078. } else
  2079. {
  2080. dwStatus = ERROR_DATABASE_BACKUP_CORRUPT;
  2081. TIME_PRINT(("CopyCptFileToClusDirp: Backup procedure not fully successful, can't restore checkpoint to CLUSDB, Error = %d !!!\n",
  2082. dwStatus));
  2083. }
  2084. goto FnExit;
  2085. }
  2086. FindClose ( hFindFile );
  2087. lstrcatW( szSourcePathName, L"chk*.tmp" );
  2088. //
  2089. // Try to find the first chk*.tmp file in the directory
  2090. //
  2091. hFindFile = FindFirstFileW( szSourcePathName, &FindData );
  2092. //
  2093. // Reuse the source path name variable
  2094. //
  2095. szSourcePathName[dwLen] = L'\0';
  2096. if ( hFindFile == INVALID_HANDLE_VALUE )
  2097. {
  2098. dwStatus = GetLastError();
  2099. TIME_PRINT(("CopyCptFileToClusDirp: Error %d in trying to find chk*.tmp file in path %ws\r\n",
  2100. szSourcePathName,
  2101. dwStatus));
  2102. goto FnExit;
  2103. }
  2104. szSourceFileName = (LPWSTR) LocalAlloc ( LMEM_FIXED,
  2105. ( lstrlenW ( szSourcePathName ) + MAX_PATH ) *
  2106. sizeof ( WCHAR ) );
  2107. if ( szSourceFileName == NULL )
  2108. {
  2109. dwStatus = GetLastError();
  2110. TIME_PRINT(("CopyCptFileToClusDirp: Error %d in allocating memory for source file name\n",
  2111. dwStatus));
  2112. goto FnExit;
  2113. }
  2114. dwStatus = ERROR_SUCCESS;
  2115. liMaxFileCreationTime.QuadPart = 0;
  2116. //
  2117. // Now, find the most recent chk*.tmp file from the source path
  2118. //
  2119. while ( dwStatus == ERROR_SUCCESS )
  2120. {
  2121. if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  2122. {
  2123. goto skip;
  2124. }
  2125. lstrcpyW( szSourceFileName, szSourcePathName );
  2126. lstrcatW( szSourceFileName, FindData.cFileName );
  2127. if ( !GetFileAttributesExW( szSourceFileName,
  2128. GetFileExInfoStandard,
  2129. &FileAttributes ) )
  2130. {
  2131. dwStatus = GetLastError();
  2132. TIME_PRINT(("CopyCptFileToClusDirp: Error %d in getting file attributes for %ws\n",
  2133. dwStatus,
  2134. szSourceFileName));
  2135. goto FnExit;
  2136. }
  2137. liFileCreationTime.HighPart = FileAttributes.ftCreationTime.dwHighDateTime;
  2138. liFileCreationTime.LowPart = FileAttributes.ftCreationTime.dwLowDateTime;
  2139. if ( liFileCreationTime.QuadPart > liMaxFileCreationTime.QuadPart )
  2140. {
  2141. liMaxFileCreationTime.QuadPart = liFileCreationTime.QuadPart;
  2142. lstrcpyW( szCheckpointFileName, FindData.cFileName );
  2143. }
  2144. skip:
  2145. if ( FindNextFileW( hFindFile, &FindData ) )
  2146. {
  2147. dwStatus = ERROR_SUCCESS;
  2148. } else
  2149. {
  2150. dwStatus = GetLastError();
  2151. }
  2152. }
  2153. if ( dwStatus == ERROR_NO_MORE_FILES )
  2154. {
  2155. dwStatus = ERROR_SUCCESS;
  2156. } else
  2157. {
  2158. TIME_PRINT(("CopyCptFileToClusDirp: FindNextFile failed\n"));
  2159. goto FnExit;
  2160. }
  2161. //
  2162. // Get the directory where the cluster is installed
  2163. //
  2164. if ( ( dwStatus = ClRtlGetClusterDirectory( szClusterDir, MAX_PATH ) )
  2165. != ERROR_SUCCESS )
  2166. {
  2167. TIME_PRINT(("CopyCptFileToClusDirp: Error %d in getting cluster dir !!!\n",
  2168. dwStatus));
  2169. goto FnExit;
  2170. }
  2171. lstrcpyW( szSourceFileName, szSourcePathName );
  2172. lstrcatW( szSourceFileName, szCheckpointFileName );
  2173. lstrcpyW( szDestFileName, szClusterDir );
  2174. dwLen = lstrlenW( szDestFileName );
  2175. if ( szDestFileName[dwLen-1] != L'\\' )
  2176. {
  2177. szDestFileName[dwLen++] = L'\\';
  2178. szDestFileName[dwLen] = L'\0';
  2179. }
  2180. #ifdef OLD_WAY
  2181. lstrcatW ( szDestFileName, L"CLUSDB" );
  2182. #else // OLD_WAY
  2183. lstrcatW ( szDestFileName, CLUSTER_DATABASE_NAME );
  2184. #endif // OLD_WAY
  2185. //
  2186. // Set the destination file attribute to normal. Continue even
  2187. // if you fail in this step because you will fail in the
  2188. // copy if this error is fatal.
  2189. //
  2190. SetFileAttributesW( szDestFileName, FILE_ATTRIBUTE_NORMAL );
  2191. //
  2192. // Now try to copy the checkpoint file to CLUSDB
  2193. //
  2194. dwStatus = CopyFileW( szSourceFileName, szDestFileName, FALSE );
  2195. if ( !dwStatus )
  2196. {
  2197. //
  2198. // You failed in copying. Check whether you encountered a
  2199. // sharing violation. If so, try unloading the cluster hive and
  2200. // then retry.
  2201. //
  2202. dwStatus = GetLastError();
  2203. if ( dwStatus == ERROR_SHARING_VIOLATION )
  2204. {
  2205. dwStatus = UnloadClusterHivep( );
  2206. if ( dwStatus == ERROR_SUCCESS )
  2207. {
  2208. SetFileAttributesW( szDestFileName, FILE_ATTRIBUTE_NORMAL );
  2209. dwStatus = CopyFileW( szSourceFileName, szDestFileName, FALSE );
  2210. if ( !dwStatus )
  2211. {
  2212. dwStatus = GetLastError();
  2213. TIME_PRINT(("CopyCptFileToClusDirp: Unable to copy file %ws to %ws for a second time, Error = %d\n",
  2214. szSourceFileName,
  2215. szDestFileName,
  2216. dwStatus));
  2217. goto FnExit;
  2218. }
  2219. } else
  2220. {
  2221. TIME_PRINT(("CopyCptFileToClusDirp: Unable to unload cluster hive, Error = %d\n",
  2222. dwStatus));
  2223. goto FnExit;
  2224. }
  2225. } else
  2226. {
  2227. TIME_PRINT(("CopyCptFileToClusDirp: Unable to copy file %ws to %ws for the first time, Error = %d\n",
  2228. szSourceFileName,
  2229. szDestFileName,
  2230. dwStatus));
  2231. goto FnExit;
  2232. }
  2233. }
  2234. //
  2235. // Set the destination file attribute to normal.
  2236. //
  2237. if ( !SetFileAttributesW( szDestFileName, FILE_ATTRIBUTE_NORMAL ) )
  2238. {
  2239. dwStatus = GetLastError();
  2240. TIME_PRINT(("CopyCptFileToClusDirp: Unable to change the %ws attributes to normal, Error = %d!\n",
  2241. szDestFileName,
  2242. dwStatus));
  2243. goto FnExit;
  2244. }
  2245. dwStatus = ERROR_SUCCESS;
  2246. FnExit:
  2247. if ( hFindFile != INVALID_HANDLE_VALUE )
  2248. {
  2249. FindClose( hFindFile );
  2250. }
  2251. LocalFree( szSourcePathName );
  2252. LocalFree( szSourceFileName );
  2253. return( dwStatus );
  2254. }
  2255. /****
  2256. @func DWORD | UnloadClusterHivep | Unload the cluster hive
  2257. @rdesc Returns a Win32 error code if the operation is
  2258. unsuccessful. ERROR_SUCCESS on success.
  2259. @xref <f CopyCptFileToClusDirp>
  2260. ****/
  2261. DWORD
  2262. UnloadClusterHivep(
  2263. VOID
  2264. )
  2265. {
  2266. BOOLEAN bWasEnabled;
  2267. DWORD dwStatus;
  2268. //
  2269. // Chittur Subbaraman (chitturs) - 10/29/98
  2270. //
  2271. dwStatus = ClRtlEnableThreadPrivilege( SE_RESTORE_PRIVILEGE,
  2272. &bWasEnabled );
  2273. if ( dwStatus != ERROR_SUCCESS )
  2274. {
  2275. if ( dwStatus == STATUS_PRIVILEGE_NOT_HELD )
  2276. {
  2277. TIME_PRINT(("UnloadClusterHivep: Restore privilege not held by client\n"));
  2278. } else
  2279. {
  2280. TIME_PRINT(("UnloadClusterHivep: Attempt to enable restore privilege failed, Error = %d\n",
  2281. dwStatus));
  2282. }
  2283. goto FnExit;
  2284. }
  2285. dwStatus = RegUnLoadKeyW( HKEY_LOCAL_MACHINE,
  2286. CLUSREG_KEYNAME_CLUSTER );
  2287. ClRtlRestoreThreadPrivilege( SE_RESTORE_PRIVILEGE,
  2288. bWasEnabled );
  2289. FnExit:
  2290. return( dwStatus );
  2291. }
  2292. DWORD
  2293. WINAPI
  2294. AddRefToClusterHandle(
  2295. IN HCLUSTER hCluster
  2296. )
  2297. /*++
  2298. Routine Description:
  2299. Increases the reference count on a cluster handle. This is done by incrementing the reference
  2300. count on the cluster handle.
  2301. Arguments:
  2302. hCluster - Cluster handle.
  2303. Return Value:
  2304. ERROR_SUCCESS if the operation succeeded.
  2305. ERROR_INVALID_HANDLE if the operation failed.
  2306. --*/
  2307. {
  2308. DWORD nStatus = ERROR_SUCCESS;
  2309. PCLUSTER pCluster = GET_CLUSTER( hCluster );
  2310. HCLUSTER hCluster2 = NULL;
  2311. //
  2312. // If this is not a valid cluster handle, don't duplicate it.
  2313. // Otherwise, increment the reference count.
  2314. //
  2315. if ( pCluster == NULL ) {
  2316. nStatus = ERROR_INVALID_HANDLE;
  2317. } else {
  2318. EnterCriticalSection( &pCluster->Lock );
  2319. pCluster->ReferenceCount++;
  2320. LeaveCriticalSection( &pCluster->Lock );
  2321. hCluster2 = hCluster;
  2322. }
  2323. return( nStatus );
  2324. } // AddRefToClusterHandle()
  2325. DWORD
  2326. WINAPI
  2327. SetClusterServiceAccountPassword(
  2328. IN LPCWSTR lpszClusterName,
  2329. IN LPCWSTR lpszNewPassword,
  2330. IN DWORD dwFlags,
  2331. OUT PCLUSTER_SET_PASSWORD_STATUS lpReturnStatusBuffer,
  2332. IN OUT LPDWORD lpcbReturnStatusBufferSize
  2333. )
  2334. /*++
  2335. Routine Description:
  2336. Updates the password used to logon the Cluster Service to its user
  2337. account. This routine updates the Service Control Manager (SCM)
  2338. Database and the Local Security Authority (LSA) password cache on
  2339. every active node of the target cluster. The execution status of the
  2340. update for each node in the cluster is returned.
  2341. Argument:
  2342. lpszClusterName
  2343. [IN] Pointer to a null-terminated Unicode string containing the
  2344. name of the cluster or one of the cluster nodes expressed
  2345. as a NetBIOS name, a fully-qualified DNS name, or an IP
  2346. address.
  2347. lpszNewPassword
  2348. [IN] Pointer to a null-terminated Unicode string containing the
  2349. new password.
  2350. dwFlags
  2351. [IN] Describing how the password update should be made to
  2352. the cluster. The dwFlags parameter is optional. If set, the
  2353. following value is valid:
  2354. CLUSTER_SET_PASSWORD_IGNORE_DOWN_NODES
  2355. Apply the update even if some nodes are not
  2356. actively participating in the cluster (i.e. not
  2357. ClusterNodeStateUp or ClusterNodeStatePaused).
  2358. By default, the update is only applied if all
  2359. nodes are up.
  2360. lpReturnStatusBuffer
  2361. [OUT] Pointer to an output buffer to receive an array containing
  2362. the execution status of the update for each node in the
  2363. cluster, or NULL if no output date is required. If
  2364. lpReturnStatusBuffer is NULL, no error is returned, and
  2365. the function stores the size of the return data, in bytes,
  2366. in the DWORD value pointed to by lpcbReturnStatusBufferSize.
  2367. This lets an application unambiguously determine the correct
  2368. return buffer size.
  2369. lpcbReturnStatusBufferSize
  2370. [IN, OUT] Pointer to a variable that on input specifies the allocated
  2371. size, in bytes, of lpReturnStatusBuffer. On output, this variable
  2372. recieves the count of bytes written to lpReturnStatusBuffer.
  2373. Return Value:
  2374. ERROR_SUCCESS
  2375. The operation was successful. The lpcbReturnStatusBufferSize
  2376. parameter points to the actual size of the data returned in the
  2377. output buffer.
  2378. ERROR_MORE_DATA
  2379. The output buffer pointed to by lpReturnStatusBuffer was not large
  2380. enough to hold the data resulting from the operation. The variable
  2381. pointed to by the lpcbReturnStatusBufferSize parameter receives the
  2382. size required for the output buffer.
  2383. ERROR_CLUSTER_OLD_VERSION
  2384. One or more nodes in the cluster are running a version of Windows
  2385. that does not support this operation.
  2386. ERROR_ALL_NODES_NOT_AVAILABLE.
  2387. Some nodes in the cluster are not available (i.e. not in the
  2388. ClusterNodeStateUp or ClusterNodeStatePaused states) and the
  2389. CLUSTER_SET_PASSWORD_IGNORE_DOWN_NODES flag is not set in dwFlags.
  2390. ERROR_FILE_CORRUPT
  2391. The encrypted new password was modified during transmission
  2392. on the network.
  2393. CRYPT_E_HASH_VALUE
  2394. The keys used by two or more nodes to encrypt the new password for
  2395. transmission on the network do not match.
  2396. ERROR_INVALID_PARAMETER.
  2397. The lpcbReturnStatusBufferSize parameter was set to NULL.
  2398. Other Win32 Error
  2399. The operation was not successful. The value specified by
  2400. lpcbReturnStatusBufferSize is unreliable.
  2401. Notes:
  2402. This function does not update the password stored by the domain
  2403. controllers for the Cluster Service's user account.
  2404. --*/
  2405. {
  2406. PCLUSTER Cluster;
  2407. DWORD Status;
  2408. PIDL_CLUSTER_SET_PASSWORD_STATUS RetReturnStatusBuffer;
  2409. DWORD RetReturnStatusBufferSize;
  2410. DWORD RetSizeReturned = 0;
  2411. DWORD RetExpectedBufferSize = 0;
  2412. HCLUSTER hCluster;
  2413. IDL_CLUSTER_SET_PASSWORD_STATUS Dummy;
  2414. if (lpcbReturnStatusBufferSize == NULL) {
  2415. return ERROR_INVALID_PARAMETER;
  2416. }
  2417. hCluster = OpenClusterAuthInfo(
  2418. lpszClusterName,
  2419. RPC_C_AUTHN_LEVEL_PKT_PRIVACY
  2420. );
  2421. if (hCluster == NULL)
  2422. {
  2423. TIME_PRINT((
  2424. "Failed to open handle to cluster %ws, status %d.\n",
  2425. (lpszClusterName == NULL) ? L"<NULL>" : lpszClusterName,
  2426. GetLastError()
  2427. ));
  2428. return GetLastError();
  2429. }
  2430. Cluster = GET_CLUSTER(hCluster);
  2431. if (Cluster == NULL)
  2432. {
  2433. CloseCluster(hCluster);
  2434. return (ERROR_INVALID_HANDLE);
  2435. }
  2436. if (lpReturnStatusBuffer == NULL)
  2437. {
  2438. ZeroMemory(&Dummy, sizeof(Dummy));
  2439. RetReturnStatusBuffer = &Dummy;
  2440. RetReturnStatusBufferSize = 0;
  2441. }
  2442. else
  2443. {
  2444. RetReturnStatusBuffer = (PIDL_CLUSTER_SET_PASSWORD_STATUS)
  2445. lpReturnStatusBuffer;
  2446. RetReturnStatusBufferSize = *lpcbReturnStatusBufferSize;
  2447. }
  2448. WRAP(Status,
  2449. (ApiSetServiceAccountPassword(
  2450. Cluster->RpcBinding,
  2451. (LPWSTR) lpszNewPassword,
  2452. dwFlags,
  2453. RetReturnStatusBuffer,
  2454. ( RetReturnStatusBufferSize /
  2455. sizeof(IDL_CLUSTER_SET_PASSWORD_STATUS)
  2456. ), // convert bytes to elements
  2457. &RetSizeReturned,
  2458. &RetExpectedBufferSize
  2459. )
  2460. ),
  2461. Cluster);
  2462. // Return status can not be ERROR_INVALID_HANDLE, since this will trigger the
  2463. // re-try logic at the RPC client. So ERROR_INVALID_HANDLE is converted to some
  2464. // value, which no Win32 function will ever set its return status to, before
  2465. // it is sent back to RPC client.
  2466. // Error codes are 32-bit values (bit 31 is the most significant bit). Bit 29
  2467. // is reserved for application-defined error codes; no system error code has
  2468. // this bit set. If you are defining an error code for your application, set this
  2469. // bit to one. That indicates that the error code has been defined by an application,
  2470. // and ensures that your error code does not conflict with any error codes defined
  2471. // by the system.
  2472. if ( Status == (ERROR_INVALID_HANDLE | 0x20000000) ) {
  2473. Status = ERROR_INVALID_HANDLE; // turn off Bit 29
  2474. }
  2475. if (Status == ERROR_SUCCESS) {
  2476. //
  2477. // Convert elements to bytes
  2478. //
  2479. *lpcbReturnStatusBufferSize = RetSizeReturned *
  2480. sizeof(CLUSTER_SET_PASSWORD_STATUS);
  2481. }
  2482. else if (Status == ERROR_MORE_DATA)
  2483. {
  2484. //
  2485. // lpReturnStatusBuffer isn't big enough. Return the required size.
  2486. // Convert from elements to bytes.
  2487. //
  2488. *lpcbReturnStatusBufferSize = RetExpectedBufferSize *
  2489. sizeof(CLUSTER_SET_PASSWORD_STATUS);
  2490. if (lpReturnStatusBuffer == NULL)
  2491. {
  2492. //
  2493. // This was a query for the required buffer size.
  2494. // Follow convention for return value.
  2495. //
  2496. Status = ERROR_SUCCESS;
  2497. }
  2498. }
  2499. else if (Status == RPC_S_PROCNUM_OUT_OF_RANGE) {
  2500. //
  2501. // Trying to talk to a W2K or NT4 cluster.
  2502. // Return a more useful error code.
  2503. //
  2504. Status = ERROR_CLUSTER_OLD_VERSION;
  2505. }
  2506. if (!CloseCluster(hCluster)) {
  2507. TIME_PRINT((
  2508. "Warning: Failed to close cluster handle, status %d.\n",
  2509. GetLastError()
  2510. ));
  2511. }
  2512. return(Status);
  2513. } //SetClusterServiceAccountPassword()