/*++ Copyright (c) 1996-1997 Microsoft Corporation Module Name: cluster.c Abstract: Server side support for Cluster APIs dealing with the whole cluster. Author: John Vert (jvert) 9-Feb-1996 Revision History: --*/ #include "apip.h" #include "clusverp.h" #include "clusudef.h" HCLUSTER_RPC s_ApiOpenCluster( IN handle_t IDL_handle, OUT error_status_t *Status ) /*++ Routine Description: Opens a handle to the cluster. This context handle is currently used only to handle cluster notify additions and deletions correctly. Added call to ApipConnectCallback which checks that connecting users have rights to open cluster. Rod Sharper 03/27/97 Arguments: IDL_handle - RPC binding handle, not used. Status - Returns any error that may occur. Return Value: A context handle to a cluster object if successful NULL otherwise. History: RodSh 27-Mar-1997 Modified to support secured user connections. --*/ { PAPI_HANDLE Handle; if ( CsUseAuthenticatedRPC ) { // if user was not granted access don't return handle *Status = ApipConnectCallback( NULL, IDL_handle ); if( *Status != RPC_S_OK ){ SetLastError( *Status ); return NULL; } } Handle = LocalAlloc(LMEM_FIXED, sizeof(API_HANDLE)); if (Handle == NULL) { CL_UNEXPECTED_ERROR( ERROR_NOT_ENOUGH_MEMORY ); *Status = ERROR_NOT_ENOUGH_MEMORY; return(NULL); } *Status = ERROR_SUCCESS; Handle->Type = API_CLUSTER_HANDLE; Handle->Flags = 0; Handle->Cluster = NULL; InitializeListHead(&Handle->NotifyList); return(Handle); } error_status_t s_ApiCloseCluster( IN OUT HCLUSTER_RPC *phCluster ) /*++ Routine Description: Closes an open cluster context handle. Arguments: phCluster - Supplies a pointer to the HCLUSTER_RPC to be closed. Returns NULL Return Value: None. --*/ { PAPI_HANDLE Handle; Handle = (PAPI_HANDLE)*phCluster; if (Handle->Type != API_CLUSTER_HANDLE) { return(ERROR_INVALID_HANDLE); } ApipRundownNotify(Handle); LocalFree(*phCluster); *phCluster = NULL; return(ERROR_SUCCESS); } VOID HCLUSTER_RPC_rundown( IN HCLUSTER_RPC Cluster ) /*++ Routine Description: RPC rundown procedure for a HCLUSTER_RPC. Just closes the handle. Arguments: Cluster - Supplies the HCLUSTER_RPC that is to be rundown. Return Value: None. --*/ { s_ApiCloseCluster(&Cluster); } error_status_t s_ApiSetClusterName( IN handle_t IDL_handle, IN LPCWSTR NewClusterName ) /*++ Routine Description: Changes the current cluster's name. Arguments: IDL_handle - RPC binding handle, not used NewClusterName - Supplies the new name of the cluster. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { DWORD Status = ERROR_SUCCESS; DWORD dwSize; LPWSTR pszClusterName = NULL; API_CHECK_INIT(); // // Get the cluster name, which is kept in the root of the // cluster registry under the "ClusterName" value, call the // FM only if the new name is different // dwSize = (MAX_COMPUTERNAME_LENGTH+1)*sizeof(WCHAR); retry: pszClusterName = (LPWSTR)LocalAlloc(LMEM_FIXED, dwSize); if (pszClusterName == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } Status = DmQueryValue(DmClusterParametersKey, CLUSREG_NAME_CLUS_NAME, NULL, (LPBYTE)pszClusterName, &dwSize); if (Status == ERROR_MORE_DATA) { // // Try again with a bigger buffer. // LocalFree(pszClusterName); goto retry; } if ( Status == ERROR_SUCCESS ) { LPWSTR pszNewNameUpperCase = NULL; pszNewNameUpperCase = (LPWSTR) LocalAlloc( LMEM_FIXED, (lstrlenW(NewClusterName) + 1) * sizeof(*NewClusterName) ); if (pszNewNameUpperCase != NULL) { lstrcpyW( pszNewNameUpperCase, NewClusterName ); _wcsupr( pszNewNameUpperCase ); Status = ApipValidateClusterName( pszNewNameUpperCase ); if ( Status == ERROR_SUCCESS ) { Status = FmChangeClusterName(pszNewNameUpperCase); } LocalFree( pszNewNameUpperCase ); } else { Status = ERROR_NOT_ENOUGH_MEMORY; } } FnExit: if ( pszClusterName ) LocalFree( pszClusterName ); return(Status); } error_status_t s_ApiGetClusterName( IN handle_t IDL_handle, OUT LPWSTR *ClusterName, OUT LPWSTR *NodeName ) /*++ Routine Description: Returns the current cluster name and the name of the node this RPC connection is to. Arguments: IDL_handle - RPC binding handle, not used ClusterName - Returns a pointer to the cluster name. This memory must be freed by the client side. NodeName - Returns a pointer to the node name. This memory must be freed by the client side. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ { DWORD Size; DWORD Status=ERROR_SUCCESS; // // Get the current node name // *ClusterName = NULL; Size = MAX_COMPUTERNAME_LENGTH+1; *NodeName = MIDL_user_allocate(Size*sizeof(WCHAR)); if (*NodeName == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } GetComputerNameW(*NodeName, &Size); // // Get the cluster name, which is kept in the root of the // cluster registry under the "ClusterName" value. // Status = ERROR_SUCCESS; Size = (MAX_COMPUTERNAME_LENGTH+1)*sizeof(WCHAR); retry: *ClusterName = MIDL_user_allocate(Size); if (*ClusterName == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } Status = DmQueryValue(DmClusterParametersKey, CLUSREG_NAME_CLUS_NAME, NULL, (LPBYTE)*ClusterName, &Size); if (Status == ERROR_MORE_DATA) { // // Try again with a bigger buffer. // MIDL_user_free(*ClusterName); goto retry; } FnExit: if (Status == ERROR_SUCCESS) { return(ERROR_SUCCESS); } if (*NodeName) MIDL_user_free(*NodeName); if (*ClusterName) MIDL_user_free(*ClusterName); *NodeName = NULL; *ClusterName = NULL; return(Status); } error_status_t s_ApiGetClusterVersion( IN handle_t IDL_handle, OUT LPWORD lpwMajorVersion, OUT LPWORD lpwMinorVersion, OUT LPWORD lpwBuildNumber, OUT LPWSTR *lpszVendorId, OUT LPWSTR *lpszCSDVersion ) /*++ Routine Description: Returns the current cluster version information. Arguments: IDL_handle - RPC binding handle, not used lpdwMajorVersion - Returns the major version number of the cluster software lpdwMinorVersion - Returns the minor version number of the cluster software lpszVendorId - Returns a pointer to the vendor name. This memory must be freed by the client side. lpszCSDVersion - Returns a pointer to the current CSD description. This memory must be freed by the client side. N.B. The CSD Version of a cluster is currently the same as the CSD Version of the base operating system. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise. --*/ { LPWSTR VendorString; LPWSTR CsdString; DWORD Length; OSVERSIONINFO OsVersionInfo; Length = lstrlenA(VER_CLUSTER_PRODUCTNAME_STR)+1; VendorString = MIDL_user_allocate(Length*sizeof(WCHAR)); if (VendorString == NULL) { return (ERROR_NOT_ENOUGH_MEMORY); } mbstowcs(VendorString, VER_CLUSTER_PRODUCTNAME_STR, Length); OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionExW(&OsVersionInfo); Length = lstrlenW(OsVersionInfo.szCSDVersion)+1; CsdString = MIDL_user_allocate(Length*sizeof(WCHAR)); if (CsdString == NULL) { return (ERROR_NOT_ENOUGH_MEMORY); } lstrcpyW(CsdString, OsVersionInfo.szCSDVersion); *lpszCSDVersion = CsdString; *lpszVendorId = VendorString; *lpwMajorVersion = VER_PRODUCTVERSION_W >> 8; *lpwMinorVersion = VER_PRODUCTVERSION_W & 0xff; *lpwBuildNumber = (WORD)(CLUSTER_GET_MINOR_VERSION(CsMyHighestVersion)); return(ERROR_SUCCESS); } error_status_t s_ApiGetClusterVersion2( IN handle_t IDL_handle, OUT LPWORD lpwMajorVersion, OUT LPWORD lpwMinorVersion, OUT LPWORD lpwBuildNumber, OUT LPWSTR *lpszVendorId, OUT LPWSTR *lpszCSDVersion, OUT PCLUSTER_OPERATIONAL_VERSION_INFO *ppClusterOpVerInfo ) /*++ Routine Description: Returns the current cluster version information. Arguments: IDL_handle - RPC binding handle, not used lpdwMajorVersion - Returns the major version number of the cluster software lpdwMinorVersion - Returns the minor version number of the cluster software lpszVendorId - Returns a pointer to the vendor name. This memory must be freed by the client side. lpszCSDVersion - Returns a pointer to the current CSD description. This memory must be freed by the client side. N.B. The CSD Version of a cluster is currently the same as the CSD Version of the base operating system. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise. --*/ { LPWSTR VendorString = NULL; LPWSTR CsdString = NULL; DWORD Length; OSVERSIONINFO OsVersionInfo; DWORD dwStatus; PCLUSTER_OPERATIONAL_VERSION_INFO pClusterOpVerInfo=NULL; *lpszVendorId = NULL; *lpszCSDVersion = NULL; *ppClusterOpVerInfo = NULL; Length = lstrlenA(VER_CLUSTER_PRODUCTNAME_STR)+1; VendorString = MIDL_user_allocate(Length*sizeof(WCHAR)); if (VendorString == NULL) { dwStatus = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } mbstowcs(VendorString, VER_CLUSTER_PRODUCTNAME_STR, Length); OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionExW(&OsVersionInfo); Length = lstrlenW(OsVersionInfo.szCSDVersion)+1; CsdString = MIDL_user_allocate(Length*sizeof(WCHAR)); if (CsdString == NULL) { dwStatus = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } lstrcpyW(CsdString, OsVersionInfo.szCSDVersion); pClusterOpVerInfo = MIDL_user_allocate(sizeof(CLUSTER_OPERATIONAL_VERSION_INFO)); if (pClusterOpVerInfo == NULL) { dwStatus = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } pClusterOpVerInfo->dwSize = sizeof(CLUSTER_OPERATIONAL_VERSION_INFO); pClusterOpVerInfo->dwReserved = 0; dwStatus = NmGetClusterOperationalVersion(&(pClusterOpVerInfo->dwClusterHighestVersion), &(pClusterOpVerInfo->dwClusterLowestVersion), &(pClusterOpVerInfo->dwFlags)); *lpszCSDVersion = CsdString; *lpszVendorId = VendorString; *ppClusterOpVerInfo = pClusterOpVerInfo; *lpwMajorVersion = VER_PRODUCTVERSION_W >> 8; *lpwMinorVersion = VER_PRODUCTVERSION_W & 0xff; *lpwBuildNumber = (WORD)CLUSTER_GET_MINOR_VERSION(CsMyHighestVersion); FnExit: if (dwStatus != ERROR_SUCCESS) { // free the strings if (VendorString) MIDL_user_free(VendorString); if (CsdString) MIDL_user_free(CsdString); if (pClusterOpVerInfo) MIDL_user_free(pClusterOpVerInfo); } return(ERROR_SUCCESS); } error_status_t s_ApiGetQuorumResource( IN handle_t IDL_handle, OUT LPWSTR *ppszResourceName, OUT LPWSTR *ppszClusFileRootPath, OUT DWORD *pdwMaxQuorumLogSize ) /*++ Routine Description: Gets the current cluster quorum resource. Arguments: IDL_handle - RPC binding handle, not used. *ppszResourceName - Returns a pointer to the current quorum resource name. This memory must be freed by the client side. *ppszClusFileRootPath - Returns the root path where the permanent cluster files are stored. *pdwMaxQuorumLogSize - Returns the size at which the quorum log path is set. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise. --*/ { DWORD Status; LPWSTR quorumId = NULL; DWORD idMaxSize = 0; DWORD idSize = 0; PFM_RESOURCE pResource=NULL; LPWSTR pszResourceName=NULL; LPWSTR pszClusFileRootPath=NULL; LPWSTR pszLogPath=NULL; LPWSTR pszEndDeviceName; API_CHECK_INIT(); // // Get the quorum resource value. // Status = DmQuerySz( DmQuorumKey, CLUSREG_NAME_QUORUM_RESOURCE, (LPWSTR*)&quorumId, &idMaxSize, &idSize); if (Status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_ERROR, "[API] s_ApiGetQuorumResource Failed to get quorum resource, error %1!u!.\n", Status); goto FnExit; } // // Reference the specified resource ID. // pResource = OmReferenceObjectById( ObjectTypeResource, quorumId ); if (pResource == NULL) { Status = ERROR_RESOURCE_NOT_FOUND; ClRtlLogPrint(LOG_ERROR, "[API] s_ApiGetQuorumResource Failed to find quorum resource object, error %1!u!\n", Status); goto FnExit; } // // Allocate buffer for returning the resource name. // pszResourceName = MIDL_user_allocate((lstrlenW(OmObjectName(pResource))+1)*sizeof(WCHAR)); if (pszResourceName == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } lstrcpyW(pszResourceName, OmObjectName(pResource)); // // Get the root path for cluster temporary files // idMaxSize = 0; idSize = 0; Status = DmQuerySz( DmQuorumKey, cszPath, (LPWSTR*)&pszLogPath, &idMaxSize, &idSize); if (Status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_ERROR, "[API] s_ApiGetQuorumResource Failed to get the log path, error %1!u!.\n", Status); goto FnExit; } // // Allocate buffer for returning the resource name. // pszClusFileRootPath = MIDL_user_allocate((lstrlenW(pszLogPath)+1)*sizeof(WCHAR)); if (pszClusFileRootPath == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } lstrcpyW(pszClusFileRootPath, pszLogPath); *ppszResourceName = pszResourceName; *ppszClusFileRootPath = pszClusFileRootPath; DmGetQuorumLogMaxSize(pdwMaxQuorumLogSize); FnExit: if (pResource) OmDereferenceObject(pResource); if (pszLogPath) LocalFree(pszLogPath); if (quorumId) LocalFree(quorumId); if (Status != ERROR_SUCCESS) { if (pszResourceName) MIDL_user_free(pszResourceName); if (pszClusFileRootPath) MIDL_user_free(pszClusFileRootPath); } return(Status); } error_status_t s_ApiSetQuorumResource( IN HRES_RPC hResource, IN LPCWSTR lpszClusFileRootPath, IN DWORD dwMaxQuorumLogSize ) /*++ Routine Description: Sets the current cluster quorum resource. Arguments: hResource - Supplies a handle to the resource that should be the cluster quorum resource. lpszClusFileRootPath - The root path for storing permananent cluster maintenace files. dwMaxQuorumLogSize - The maximum size of the quorum logs before they are reset by checkpointing. If 0, the default is used. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise. --*/ { DWORD Status; PFM_RESOURCE Resource; LPCWSTR lpszPathName = NULL; API_CHECK_INIT(); VALIDATE_RESOURCE_EXISTS(Resource, hResource); // // Chittur Subbaraman (chitturs) - 1/6/99 // // Check whether the user is passing in a pointer to a NULL character // as the second parameter. If not, pass the parameter passed by the // user // if ( ( ARGUMENT_PRESENT( lpszClusFileRootPath ) ) && ( *lpszClusFileRootPath != L'\0' ) ) { lpszPathName = lpszClusFileRootPath; } // // Let FM decide if this operation can be completed. // Status = FmSetQuorumResource(Resource, lpszPathName, dwMaxQuorumLogSize ); if ( Status != ERROR_SUCCESS ) { return(Status); } //Update the path return(Status); } error_status_t s_ApiSetNetworkPriorityOrder( IN handle_t IDL_handle, IN DWORD NetworkCount, IN LPWSTR *NetworkIdList ) /*++ Routine Description: Sets the priority order for internal (intracluster) networks. Arguments: IDL_handle - RPC binding handle, not used NetworkCount - The count of networks in the NetworkList NetworkList - An array of pointers to network IDs. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise. --*/ { API_CHECK_INIT(); return( NmSetNetworkPriorityOrder( NetworkCount, NetworkIdList ) ); } error_status_t s_ApiBackupClusterDatabase( IN handle_t IDL_handle, IN LPCWSTR lpszPathName ) /*++ Routine Description: Requests for backup of the quorum log file and the checkpoint file. Argument: IDL_handle - RPC binding handle, not used lpszPathName - The directory path name where the files have to be backed up. This path must be visible to the node on which the quorum resource is online. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise. --*/ { API_CHECK_INIT(); // // Let FM decide if this operation can be completed. // return( FmBackupClusterDatabase( lpszPathName ) ); } DWORD ApipValidateClusterName( IN LPCWSTR lpszNewName ) /*++ Routine Description: Check whether the new cluster name is valid Argument: lpszNewName - New cluster name. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise. --*/ { DWORD dwSize = 0; PFM_RESOURCE pResource = NULL; DWORD dwStatus; DWORD dwBytesReturned; DWORD dwRequired; LPWSTR lpszClusterNameResource = NULL; CLUSPROP_BUFFER_HELPER ListEntry; PVOID pPropList = NULL; DWORD cbListSize = 0; DWORD dwBufferSize; ClRtlLogPrint(LOG_NOISE, "[API] ApipValidateClusterName: Validating new name %1!ws!...\n", lpszNewName); dwStatus = DmQuerySz( DmClusterParametersKey, CLUSREG_NAME_CLUS_CLUSTER_NAME_RES, &lpszClusterNameResource, &dwSize, &dwSize ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_ERROR, "[API] ApipValidateClusterName: Failed to get cluster name resource from registry, error %1!u!...\n", dwStatus); goto FnExit; } // // Reference the specified resource ID. // pResource = OmReferenceObjectById( ObjectTypeResource, lpszClusterNameResource ); if ( pResource == NULL ) { dwStatus = ERROR_RESOURCE_NOT_FOUND; ClRtlLogPrint(LOG_ERROR, "[API] ApipValidateClusterName: Failed to find cluster name resource, %1!u!...\n", dwStatus); goto FnExit; } dwBufferSize = sizeof( ListEntry.pList->nPropertyCount ) + sizeof( *ListEntry.pName ) + ALIGN_CLUSPROP( ( lstrlenW( CLUSREG_NAME_NET_NAME ) + 1 ) * sizeof( WCHAR ) ) + sizeof( *ListEntry.pStringValue ) + ALIGN_CLUSPROP( ( lstrlenW( lpszNewName ) + 1 ) * sizeof( WCHAR ) ) + sizeof( *ListEntry.pSyntax ); ListEntry.pb = (PBYTE) LocalAlloc( LPTR, dwBufferSize ); if ( ListEntry.pb == NULL ) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_ERROR, "[API] ApipValidateClusterName: Error %1!u! in allocating memory...\n", dwStatus); goto FnExit; } pPropList = ListEntry.pb; ListEntry.pList->nPropertyCount = 1; cbListSize += sizeof( ListEntry.pList->nPropertyCount ); ListEntry.pb += sizeof( ListEntry.pList->nPropertyCount ); ListEntry.pName->Syntax.dw = CLUSPROP_SYNTAX_NAME; ListEntry.pName->cbLength = ( lstrlenW( CLUSREG_NAME_NET_NAME ) + 1 ) * sizeof( WCHAR ); lstrcpyW( ListEntry.pName->sz, CLUSREG_NAME_NET_NAME ); cbListSize += sizeof( *ListEntry.pName ) + ALIGN_CLUSPROP( ListEntry.pName->cbLength ); ListEntry.pb += sizeof( *ListEntry.pName ) + ALIGN_CLUSPROP( ListEntry.pName->cbLength ); ListEntry.pStringValue->Syntax.dw = CLUSPROP_SYNTAX_LIST_VALUE_SZ; ListEntry.pStringValue->cbLength = ( lstrlenW( lpszNewName ) + 1 ) * sizeof( WCHAR ); lstrcpyW( ListEntry.pStringValue->sz, lpszNewName ); cbListSize += sizeof( *ListEntry.pStringValue ) + ALIGN_CLUSPROP( ListEntry.pName->cbLength ); ListEntry.pb += sizeof( *ListEntry.pStringValue ) + ALIGN_CLUSPROP( ListEntry.pName->cbLength ); ListEntry.pSyntax->dw = CLUSPROP_SYNTAX_ENDMARK; cbListSize += sizeof( *ListEntry.pSyntax ); ListEntry.pb += sizeof( *ListEntry.pSyntax ); dwStatus = FmResourceControl( pResource, NULL, CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES, (PUCHAR)pPropList, cbListSize, NULL, 0, &dwBytesReturned, &dwRequired ); FnExit: LocalFree( lpszClusterNameResource ); LocalFree( pPropList ); if ( pResource != NULL ) { OmDereferenceObject( pResource ); } ClRtlLogPrint(LOG_NOISE, "[API] ApipValidateClusterName returns %1!u!...\n", dwStatus); return( dwStatus ); } /*++ The set service account password API was added to the cluster service after Windows XP shipped. In order to add client-side clusapi.dll support to XP SP1 without breaking the XP SP1 build, this dummy server-side routine must be added, even though this code does not ship in XP SP1. --*/ error_status_t s_ApiSetServiceAccountPassword( IN handle_t IDL_handle, IN LPWSTR lpszNewPassword, IN DWORD dwFlags, OUT IDL_CLUSTER_SET_PASSWORD_STATUS *ReturnStatusBufferPtr, IN DWORD ReturnStatusBufferSize, OUT DWORD *SizeReturned, OUT DWORD *ExpectedBufferSize ) { return( ERROR_CALL_NOT_IMPLEMENTED ); } // s_ApiSetServiceAccountPassword