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.

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