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.
1735 lines
43 KiB
1735 lines
43 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
resource.c
|
|
|
|
Abstract:
|
|
|
|
Server side support for Cluster APIs dealing with resources
|
|
|
|
Author:
|
|
|
|
John Vert (jvert) 7-Mar-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include "apip.h"
|
|
|
|
HRES_RPC
|
|
s_ApiOpenResource(
|
|
IN handle_t IDL_handle,
|
|
IN LPCWSTR lpszResourceName,
|
|
OUT error_status_t *Status
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens a handle to an existing resource object.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - RPC binding handle, not used.
|
|
|
|
lpszResourceName - Supplies the name of the resource to open.
|
|
|
|
Status - Returns any error that may occur.
|
|
|
|
Return Value:
|
|
|
|
A context handle to a resource object if successful
|
|
|
|
NULL otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRES_RPC Resource;
|
|
PAPI_HANDLE Handle;
|
|
|
|
Handle = LocalAlloc(LMEM_FIXED, sizeof(API_HANDLE));
|
|
if (Handle == NULL) {
|
|
*Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
return(NULL);
|
|
}
|
|
Resource = OmReferenceObjectByName(ObjectTypeResource, lpszResourceName);
|
|
if (Resource == NULL) {
|
|
LocalFree(Handle);
|
|
*Status = ERROR_RESOURCE_NOT_FOUND;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[API] s_ApiOpenResource: Resource %1!ws! not found, status = %2!u!...\n",
|
|
lpszResourceName,
|
|
*Status);
|
|
return(NULL);
|
|
}
|
|
*Status = ERROR_SUCCESS;
|
|
Handle->Type = API_RESOURCE_HANDLE;
|
|
Handle->Resource = Resource;
|
|
Handle->Flags = 0;
|
|
InitializeListHead(&Handle->NotifyList);
|
|
return(Handle);
|
|
}
|
|
|
|
HRES_RPC
|
|
s_ApiCreateResource(
|
|
IN HGROUP_RPC hGroup,
|
|
IN LPCWSTR lpszResourceName,
|
|
IN LPCWSTR lpszResourceType,
|
|
IN DWORD dwFlags,
|
|
OUT error_status_t *pStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a new resource object.
|
|
|
|
Arguments:
|
|
|
|
hGroup - Supplies the group the resource is to be created in.
|
|
|
|
lpszResourceName - Supplies the name of the resource to create.
|
|
|
|
lpszResourceType - Supplies the type of the resource.
|
|
|
|
dwFlags - Supplies any optional flags.
|
|
|
|
Status - Returns any error that may occur.
|
|
|
|
Return Value:
|
|
|
|
A context handle to a resource object if successful
|
|
|
|
NULL otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
HRES_RPC Resource=NULL;
|
|
PFM_GROUP Group;
|
|
UUID Guid;
|
|
DWORD Status = ERROR_SUCCESS;
|
|
WCHAR *KeyName=NULL;
|
|
HDMKEY Key=NULL;
|
|
HDMKEY GroupKey=NULL;
|
|
HDMKEY TypeKey = NULL;
|
|
HDMKEY ParamKey;
|
|
DWORD Disposition;
|
|
DWORD pollIntervals = CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL;
|
|
PAPI_HANDLE Handle;
|
|
PFM_RESTYPE ResType;
|
|
DWORD dwPersistentState = 0;
|
|
DWORD dwClusterHighestVersion;
|
|
|
|
if (ApiState != ApiStateOnline)
|
|
{
|
|
*pStatus = ERROR_SHARING_PAUSED;
|
|
return(NULL);
|
|
}
|
|
|
|
if ((hGroup == NULL) || (((PAPI_HANDLE)hGroup)->Type != API_GROUP_HANDLE))
|
|
{
|
|
*pStatus = ERROR_INVALID_HANDLE;
|
|
return(NULL);
|
|
}
|
|
Group = ((PAPI_HANDLE)hGroup)->Group;
|
|
|
|
//
|
|
// Check for bogus flags.
|
|
//
|
|
if (dwFlags & ~CLUSTER_RESOURCE_VALID_FLAGS)
|
|
{
|
|
*pStatus = ERROR_INVALID_PARAMETER;
|
|
return(NULL);
|
|
}
|
|
|
|
Handle = LocalAlloc(LMEM_FIXED, sizeof(API_HANDLE));
|
|
if (Handle == NULL)
|
|
{
|
|
*pStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 1/30/2000
|
|
//
|
|
// If we are dealing with the mixed mode cluster, do the
|
|
// registry updates right here since the GUM handler won't do it.
|
|
//
|
|
NmGetClusterOperationalVersion( &dwClusterHighestVersion,
|
|
NULL,
|
|
NULL );
|
|
|
|
//
|
|
// Open the resource type key. This validates that the specified type exists.
|
|
//
|
|
TypeKey = DmOpenKey(DmResourceTypesKey,
|
|
lpszResourceType,
|
|
KEY_READ);
|
|
if (TypeKey == NULL)
|
|
{
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
retry:
|
|
//
|
|
// Create a GUID for this resource.
|
|
//
|
|
Status = UuidCreate(&Guid);
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
goto error_exit;
|
|
}
|
|
Status = UuidToString(&Guid, &KeyName);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
goto error_exit;
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[API] Creating resource %1!ws! <%2!ws!> (%3!ws!)\n",
|
|
lpszResourceType,
|
|
lpszResourceName,
|
|
KeyName);
|
|
|
|
if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
|
|
NT51_MAJOR_VERSION )
|
|
{
|
|
//
|
|
// Create the new resource key.
|
|
//
|
|
Key = DmCreateKey(DmResourcesKey,
|
|
KeyName,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
&Disposition);
|
|
if (Key == NULL)
|
|
{
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
if (Disposition != REG_CREATED_NEW_KEY)
|
|
{
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[API] ApiCreateResource generated GUID %1!ws! that already existed! This is impossible.\n",
|
|
KeyName);
|
|
DmCloseKey(Key);
|
|
RpcStringFree(&KeyName);
|
|
goto retry;
|
|
}
|
|
|
|
CL_ASSERT(Disposition == REG_CREATED_NEW_KEY);
|
|
|
|
//
|
|
// Set the resource's name in the registry
|
|
//
|
|
Status = DmSetValue(Key,
|
|
CLUSREG_NAME_RES_NAME,
|
|
REG_SZ,
|
|
(CONST BYTE *)lpszResourceName,
|
|
(lstrlenW(lpszResourceName)+1)*sizeof(WCHAR));
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Set the resource's type in the registry
|
|
// Note we reference the resource type and use its ID
|
|
// so that the case is correct.
|
|
//
|
|
ResType = OmReferenceObjectById(ObjectTypeResType, lpszResourceType);
|
|
if ( ResType == NULL )
|
|
{
|
|
//
|
|
// Should not happen normally since we checked if the type existed in
|
|
// the registry.
|
|
//
|
|
Status = ERROR_RESOURCE_TYPE_NOT_FOUND;
|
|
goto error_exit;
|
|
}
|
|
lpszResourceType = OmObjectId(ResType);
|
|
OmDereferenceObject(ResType);
|
|
Status = DmSetValue(Key,
|
|
CLUSREG_NAME_RES_TYPE,
|
|
REG_SZ,
|
|
(CONST BYTE *)lpszResourceType,
|
|
(lstrlenW(lpszResourceType)+1)*sizeof(WCHAR));
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Set the resource's poll intervals in the registry.
|
|
//
|
|
Status = DmSetValue(Key,
|
|
CLUSREG_NAME_RES_LOOKS_ALIVE,
|
|
REG_DWORD,
|
|
(CONST BYTE *)&pollIntervals,
|
|
4);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto error_exit;
|
|
}
|
|
Status = DmSetValue(Key,
|
|
CLUSREG_NAME_RES_IS_ALIVE,
|
|
REG_DWORD,
|
|
(CONST BYTE *)&pollIntervals,
|
|
4);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// If this resource should be started in a separate monitor, set that
|
|
// parameter now.
|
|
//
|
|
if (dwFlags & CLUSTER_RESOURCE_SEPARATE_MONITOR)
|
|
{
|
|
DWORD SeparateMonitor = 1;
|
|
|
|
Status = DmSetValue(Key,
|
|
CLUSREG_NAME_RES_SEPARATE_MONITOR,
|
|
REG_DWORD,
|
|
(CONST BYTE *)&SeparateMonitor,
|
|
sizeof(SeparateMonitor));
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create a Parameters key for the resource.
|
|
//
|
|
ParamKey = DmCreateKey(Key,
|
|
CLUSREG_KEYNAME_PARAMETERS,
|
|
0,
|
|
KEY_READ,
|
|
NULL,
|
|
&Disposition);
|
|
if (ParamKey == NULL)
|
|
{
|
|
CL_LOGFAILURE(GetLastError());
|
|
} else
|
|
{
|
|
DmCloseKey(ParamKey);
|
|
}
|
|
|
|
GroupKey = DmOpenKey(DmGroupsKey, OmObjectId(Group), KEY_READ | KEY_WRITE);
|
|
if (GroupKey == NULL)
|
|
{
|
|
Status = GetLastError();
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 5/25/99
|
|
//
|
|
// Make sure you set the persistent state of the resource to
|
|
// ClusterResourceOffline before you create the resource. If
|
|
// this is not done, if you create a resource in a group which
|
|
// is online, the group's persistent state value (i.e., 1 in
|
|
// this case) is inherited by the resource in FmpQueryResourceInfo
|
|
// (only the memory state is set and not the registry state and
|
|
// this was a problem as well) and if you move such a group to
|
|
// another node, it will bring the newly created resource online.
|
|
//
|
|
Status = DmSetValue( Key,
|
|
CLUSREG_NAME_RES_PERSISTENT_STATE,
|
|
REG_DWORD,
|
|
( CONST BYTE * )&dwPersistentState,
|
|
sizeof( DWORD ) );
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
{
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
Resource = FmCreateResource(Group, KeyName, lpszResourceName, lpszResourceType, dwFlags);
|
|
|
|
if (Resource == NULL)
|
|
{
|
|
Status = GetLastError();
|
|
//
|
|
// HACKHACK: Looks like this retry loop was coded up to retry in case a new resource got the
|
|
// GUID of an existing resource. FmpUpdateCreateResource returns this error in case of a conflict.
|
|
// It is best to get rid of it since we should assume UUidCreate generates a unique ID that
|
|
// doesn't conflict with anything else. Else, it is a bug in that API. We should not be
|
|
// masking that.
|
|
//
|
|
if (Status == ERROR_ALREADY_EXISTS)
|
|
{
|
|
RpcStringFree(&KeyName);
|
|
goto retry;
|
|
}
|
|
goto error_exit;
|
|
}
|
|
|
|
if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
|
|
NT51_MAJOR_VERSION )
|
|
{
|
|
//
|
|
// Add the resource to the Contains value of the specified group.
|
|
//
|
|
Status = DmAppendToMultiSz(GroupKey,
|
|
CLUSREG_NAME_GRP_CONTAINS,
|
|
KeyName);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// BUGBUG John Vert (jvert) 3-May-1996
|
|
// Need to delete this from the FM!
|
|
//
|
|
OmDereferenceObject(Resource);
|
|
Resource = NULL;
|
|
}
|
|
}
|
|
|
|
error_exit:
|
|
if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
|
|
NT51_MAJOR_VERSION )
|
|
{
|
|
if (Key != NULL)
|
|
{
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Try and cleanup the key we just created.
|
|
//
|
|
DmDeleteKey(Key, CLUSREG_KEYNAME_PARAMETERS);
|
|
DmDeleteKey(DmResourcesKey, KeyName);
|
|
}
|
|
DmCloseKey(Key);
|
|
}
|
|
if (GroupKey != NULL)
|
|
{
|
|
DmCloseKey(GroupKey);
|
|
}
|
|
}
|
|
|
|
if (TypeKey != NULL)
|
|
{
|
|
DmCloseKey(TypeKey);
|
|
}
|
|
|
|
if (KeyName != NULL)
|
|
{
|
|
RpcStringFree(&KeyName);
|
|
}
|
|
|
|
*pStatus = Status;
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
LocalFree(Handle);
|
|
return(NULL);
|
|
}
|
|
|
|
CL_ASSERT(Resource != NULL);
|
|
Handle->Type = API_RESOURCE_HANDLE;
|
|
Handle->Resource = Resource;
|
|
Handle->Flags = 0;
|
|
InitializeListHead(&Handle->NotifyList);
|
|
return(Handle);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiDeleteResource(
|
|
IN HRES_RPC hResource
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes the specified cluster resource from the group. The resource
|
|
must have no other resources dependent on it.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the cluster resource to be deleted.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is ERROR_SUCCESS.
|
|
|
|
If the function fails, the return value is an error value.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
DWORD Status;
|
|
HDMKEY Key;
|
|
HDMKEY GroupKey;
|
|
DWORD dwClusterHighestVersion;
|
|
|
|
API_CHECK_INIT();
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 09/07/2000
|
|
//
|
|
// If we are dealing with a Whistler-Win2K cluster, do the
|
|
// registry updates right here since the GUM handler won't do it.
|
|
//
|
|
NmGetClusterOperationalVersion( &dwClusterHighestVersion,
|
|
NULL,
|
|
NULL );
|
|
|
|
Status = FmDeleteResource(Resource);
|
|
|
|
if ( ( Status == ERROR_SUCCESS ) &&
|
|
( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
|
|
NT51_MAJOR_VERSION ) ) {
|
|
Status = DmDeleteTree(DmResourcesKey,OmObjectId(Resource));
|
|
if ( (Status != ERROR_SUCCESS) &&
|
|
(Status != ERROR_FILE_NOT_FOUND) ) {
|
|
CL_LOGFAILURE( Status );
|
|
return(Status);
|
|
}
|
|
GroupKey = DmOpenKey(DmGroupsKey,
|
|
OmObjectId(Resource->Group),
|
|
KEY_READ | KEY_SET_VALUE);
|
|
if (GroupKey != NULL) {
|
|
DmRemoveFromMultiSz(GroupKey,
|
|
CLUSREG_NAME_GRP_CONTAINS,
|
|
OmObjectId(Resource));
|
|
DmCloseKey(GroupKey);
|
|
}
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiCloseResource(
|
|
IN OUT HRES_RPC *phResource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes an open resource context handle.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies a pointer to the HRES_RPC to be closed.
|
|
Returns NULL
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
PAPI_HANDLE Handle;
|
|
|
|
VALIDATE_RESOURCE(Resource, *phResource);
|
|
|
|
Handle = (PAPI_HANDLE)*phResource;
|
|
ApipRundownNotify(Handle);
|
|
OmDereferenceObject(Resource);
|
|
|
|
LocalFree(*phResource);
|
|
*phResource = NULL;
|
|
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
VOID
|
|
HRES_RPC_rundown(
|
|
IN HRES_RPC Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
RPC rundown procedure for a HRES_RPC. Just closes the handle.
|
|
|
|
Arguments:
|
|
|
|
Resource - Supplies the HRES_RPC that is to be rundown.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
s_ApiCloseResource(&Resource);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiGetResourceState(
|
|
IN HRES_RPC hResource,
|
|
OUT DWORD *lpState,
|
|
OUT LPWSTR *lpNodeId,
|
|
OUT LPWSTR *lpGroupName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the current state of the specified resource.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource whose state is to be returned.
|
|
|
|
lpState - Returns the current state of the resource
|
|
|
|
lpNodeId - Returns the Id of the node where the resource is currently online
|
|
|
|
lpGroupName - Returns the name of the group the the resource is a member of
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
LPWSTR NodeId;
|
|
DWORD IdLength;
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
|
|
IdLength = MAX_COMPUTERNAME_LENGTH+1;
|
|
NodeId = MIDL_user_allocate(IdLength*sizeof(WCHAR));
|
|
if (NodeId == NULL) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
*lpState = FmGetResourceState( Resource,
|
|
NodeId,
|
|
&IdLength);
|
|
if ( *lpState == ClusterResourceStateUnknown ) {
|
|
MIDL_user_free(NodeId);
|
|
return(GetLastError());
|
|
}
|
|
*lpNodeId = NodeId;
|
|
*lpGroupName = ApipGetObjectName(Resource->Group);
|
|
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiSetResourceName(
|
|
IN HRES_RPC hResource,
|
|
IN LPCWSTR lpszResourceName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the new friendly name of a resource.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource whose name is to be set.
|
|
|
|
lpszResourceName - Supplies the new name of hResource
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
DWORD Status;
|
|
|
|
API_CHECK_INIT();
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
|
|
//
|
|
// Tell the FM about the new name. If it is OK with the
|
|
// FM, go ahead and update the registry.
|
|
//
|
|
Status = FmSetResourceName(Resource,
|
|
lpszResourceName);
|
|
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
error_status_t
|
|
s_ApiGetResourceId(
|
|
IN HRES_RPC hResource,
|
|
OUT LPWSTR *pGuid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the unique identifier (GUID) for a resource.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource whose identifer is to be returned
|
|
|
|
pGuid - Returns the unique identifier. This memory must be freed on the
|
|
client side.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
DWORD NameLen;
|
|
LPCWSTR Name;
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
|
|
Name = OmObjectId(Resource);
|
|
|
|
NameLen = (lstrlenW(Name)+1)*sizeof(WCHAR);
|
|
*pGuid = MIDL_user_allocate(NameLen);
|
|
if (*pGuid == NULL) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
CopyMemory(*pGuid, Name, NameLen);
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiGetResourceType(
|
|
IN HRES_RPC hResource,
|
|
OUT LPWSTR *lpszResourceType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the resource type for a resource.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource whose identifer is to be returned
|
|
|
|
lpszResourceType - Returns the resource type name. This memory must be
|
|
freed on the client side.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
DWORD NameLen;
|
|
LPCWSTR Name;
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
|
|
if ( Resource->Type == NULL ) {
|
|
return(ERROR_INVALID_STATE);
|
|
}
|
|
|
|
Name = OmObjectId(Resource->Type);
|
|
|
|
NameLen = (lstrlenW(Name)+1)*sizeof(WCHAR);
|
|
*lpszResourceType = MIDL_user_allocate(NameLen);
|
|
if (*lpszResourceType == NULL) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
CopyMemory(*lpszResourceType, Name, NameLen);
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
DWORD
|
|
s_ApiOnlineResource(
|
|
IN HRES_RPC hResource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Brings a resource and all its dependencies online
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource to be brought online
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
|
|
API_CHECK_INIT();
|
|
|
|
VALIDATE_RESOURCE(Resource, hResource);
|
|
|
|
return(FmOnlineResource(Resource));
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
s_ApiFailResource(
|
|
IN HRES_RPC hResource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initiates a resource failure. The specified resource is treated as failed.
|
|
This causes the cluster to initiate the same failover process that would
|
|
result if the resource actually failed.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource to be failed over
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
|
|
API_CHECK_INIT();
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
|
|
return(FmFailResource(Resource));
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
s_ApiOfflineResource(
|
|
IN HRES_RPC hResource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Brings a resource and all its dependents offline
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource to be brought offline
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
|
|
API_CHECK_INIT();
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
|
|
return(FmOfflineResource(Resource));
|
|
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiAddResourceDependency(
|
|
IN HRES_RPC hResource,
|
|
IN HRES_RPC hDependsOn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a dependency relationship to a given resource. Both
|
|
resources must be in the same group.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource which is dependent.
|
|
|
|
hDependsOn - Supplies the resource that hResource depends on.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
Win32 error code otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
PFM_RESOURCE DependsOn;
|
|
DWORD Status;
|
|
HDMKEY ResKey;
|
|
|
|
API_CHECK_INIT();
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
VALIDATE_RESOURCE_EXISTS(DependsOn, hDependsOn);
|
|
|
|
//
|
|
// Call the FM to create the dependency relationship.
|
|
//
|
|
Status = FmAddResourceDependency(Resource, DependsOn);
|
|
if (Status == ERROR_SUCCESS) {
|
|
//
|
|
// Add the dependency information to the cluster database.
|
|
//
|
|
ResKey = DmOpenKey(DmResourcesKey,
|
|
OmObjectId(Resource),
|
|
KEY_READ | KEY_SET_VALUE);
|
|
if (ResKey == NULL) {
|
|
Status = GetLastError();
|
|
CL_LOGFAILURE(Status);
|
|
} else {
|
|
Status = DmAppendToMultiSz(ResKey,
|
|
CLUSREG_NAME_RES_DEPENDS_ON,
|
|
OmObjectId(DependsOn));
|
|
DmCloseKey(ResKey);
|
|
}
|
|
if (Status != ERROR_SUCCESS) {
|
|
FmRemoveResourceDependency(Resource, DependsOn);
|
|
}
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
error_status_t
|
|
s_ApiRemoveResourceDependency(
|
|
IN HRES_RPC hResource,
|
|
IN HRES_RPC hDependsOn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes a dependency relationship to a given resource. Both
|
|
resources must be in the same group.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource which is dependent.
|
|
|
|
hDependsOn - Supplies the resource that hResource depends on.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
Win32 error code otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
PFM_RESOURCE DependsOn;
|
|
DWORD Status;
|
|
HDMKEY ResKey;
|
|
|
|
API_CHECK_INIT();
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
VALIDATE_RESOURCE_EXISTS(DependsOn, hDependsOn);
|
|
|
|
//
|
|
// If the resources are not in the same group, fail the
|
|
// call. Also fail if some one tries to make a resource
|
|
// dependent upon itself.
|
|
//
|
|
if ((Resource->Group != DependsOn->Group) ||
|
|
(Resource == DependsOn)) {
|
|
return(ERROR_DEPENDENCY_NOT_FOUND);
|
|
}
|
|
|
|
//
|
|
// Remove the dependency from the registry database.
|
|
//
|
|
ResKey = DmOpenKey(DmResourcesKey,
|
|
OmObjectId(Resource),
|
|
KEY_READ | KEY_SET_VALUE);
|
|
if (ResKey == NULL) {
|
|
Status = GetLastError();
|
|
CL_LOGFAILURE(Status);
|
|
} else {
|
|
Status = DmRemoveFromMultiSz(ResKey,
|
|
CLUSREG_NAME_RES_DEPENDS_ON,
|
|
OmObjectId(DependsOn));
|
|
DmCloseKey(ResKey);
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Call the FM to remove the dependency relationship.
|
|
//
|
|
Status = FmRemoveResourceDependency(Resource, DependsOn);
|
|
|
|
} else if (Status == ERROR_FILE_NOT_FOUND) {
|
|
|
|
//
|
|
// Map this expected error to something a little more reasonable.
|
|
//
|
|
Status = ERROR_DEPENDENCY_NOT_FOUND;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiCanResourceBeDependent(
|
|
IN HRES_RPC hResource,
|
|
IN HRES_RPC hResourceDependent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if the resource identified by hResource can depend on hResourceDependent.
|
|
In order for this to be true, both resources must be members of the same group and
|
|
the resource identified by hResourceDependent cannot depend on the resource identified
|
|
by hResource, whether directly or indirectly.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies a handle to the resource to be dependent.
|
|
|
|
hResourceDependent - Supplies a handle to the resource on which
|
|
the resource identified by hResource can depend.
|
|
|
|
|
|
Return Value:
|
|
|
|
If the resource identified by hResource can depend on the resource
|
|
identified by hResourceDependent, the return value is ERROR_SUCCESS.
|
|
|
|
Otherwise, the return value is ERROR_DEPENDENCY_ALREADY_EXISTS.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
PFM_RESOURCE ResourceDependent;
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
VALIDATE_RESOURCE_EXISTS(ResourceDependent, hResourceDependent);
|
|
|
|
if (Resource == ResourceDependent) {
|
|
//
|
|
// The caller is confused and is trying to make something
|
|
// depend on itself.
|
|
//
|
|
return(ERROR_DEPENDENCY_ALREADY_EXISTS);
|
|
}
|
|
|
|
if (Resource->Group != ResourceDependent->Group) {
|
|
//
|
|
// The caller is confused and is trying to make something
|
|
// depend on a resource in another group.
|
|
//
|
|
return(ERROR_DEPENDENCY_ALREADY_EXISTS);
|
|
}
|
|
|
|
//
|
|
// If the dependent is a quorum resource, you can't add a dependency.
|
|
//
|
|
if ( Resource->QuorumResource ) {
|
|
return ( ERROR_DEPENDENCY_NOT_ALLOWED );
|
|
}
|
|
|
|
if (FmDependentResource(ResourceDependent, Resource, FALSE)) {
|
|
return(ERROR_DEPENDENCY_ALREADY_EXISTS);
|
|
} else {
|
|
|
|
//
|
|
// Finally check to make sure an immediate dependency does
|
|
// not already exist.
|
|
//
|
|
if (FmDependentResource(Resource, ResourceDependent, TRUE)) {
|
|
return(ERROR_DEPENDENCY_ALREADY_EXISTS);
|
|
} else {
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
error_status_t
|
|
s_ApiCreateResEnum(
|
|
IN HRES_RPC hResource,
|
|
IN DWORD dwType,
|
|
OUT PENUM_LIST *ReturnEnum
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerates all the specified resource properties and returns the
|
|
list of objects to the caller. The client-side is responsible
|
|
for freeing the allocated memory.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource whose properties are to be
|
|
enumerated.
|
|
|
|
dwType - Supplies the type of properties to be enumerated.
|
|
|
|
ReturnEnum - Returns the requested objects.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
DWORD Allocated = 0;
|
|
PENUM_LIST Enum = NULL;
|
|
DWORD i;
|
|
DWORD Result;
|
|
PFM_RESOURCE Resource;
|
|
PFM_RESOURCE Target;
|
|
PNM_NODE Node;
|
|
LPWSTR RealName;
|
|
|
|
if (dwType & ~CLUSTER_RESOURCE_ENUM_ALL) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
|
|
Allocated = INITIAL_ENUM_LIST_ALLOCATION;
|
|
Enum = MIDL_user_allocate(ENUM_SIZE(Allocated));
|
|
if (Enum == NULL) {
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
Enum->EntryCount = 0;
|
|
|
|
//
|
|
// Enumerate all dependencies.
|
|
//
|
|
if (dwType & CLUSTER_RESOURCE_ENUM_DEPENDS) {
|
|
i=0;
|
|
do {
|
|
Result = FmEnumResourceDependent(Resource,
|
|
i,
|
|
&Target);
|
|
if (Result == ERROR_SUCCESS) {
|
|
RealName = ApipGetObjectName( Target );
|
|
if (RealName != NULL) {
|
|
ApipAddToEnum(&Enum,
|
|
&Allocated,
|
|
RealName,
|
|
CLUSTER_RESOURCE_ENUM_DEPENDS);
|
|
MIDL_user_free(RealName);
|
|
}
|
|
OmDereferenceObject(Target);
|
|
++i;
|
|
}
|
|
} while ( Result == ERROR_SUCCESS );
|
|
}
|
|
|
|
//
|
|
// Enumerate all dependents
|
|
//
|
|
if (dwType & CLUSTER_RESOURCE_ENUM_PROVIDES) {
|
|
i=0;
|
|
do {
|
|
Result = FmEnumResourceProvider(Resource,
|
|
i,
|
|
&Target);
|
|
if (Result == ERROR_SUCCESS) {
|
|
RealName = ApipGetObjectName( Target );
|
|
if (RealName != NULL) {
|
|
ApipAddToEnum(&Enum,
|
|
&Allocated,
|
|
RealName,
|
|
CLUSTER_RESOURCE_ENUM_PROVIDES);
|
|
MIDL_user_free(RealName);
|
|
}
|
|
OmDereferenceObject(Target);
|
|
++i;
|
|
}
|
|
} while ( Result == ERROR_SUCCESS );
|
|
}
|
|
|
|
//
|
|
// Enumerate all possible nodes
|
|
//
|
|
if (dwType & CLUSTER_RESOURCE_ENUM_NODES) {
|
|
i=0;
|
|
do {
|
|
Result = FmEnumResourceNode(Resource,
|
|
i,
|
|
&Node);
|
|
if (Result == ERROR_SUCCESS) {
|
|
RealName = (LPWSTR)OmObjectName( Node );
|
|
if (RealName != NULL) {
|
|
ApipAddToEnum(&Enum,
|
|
&Allocated,
|
|
RealName,
|
|
CLUSTER_RESOURCE_ENUM_NODES);
|
|
}
|
|
OmDereferenceObject(Node);
|
|
++i;
|
|
}
|
|
} while ( Result == ERROR_SUCCESS );
|
|
}
|
|
|
|
*ReturnEnum = Enum;
|
|
return(ERROR_SUCCESS);
|
|
|
|
ErrorExit:
|
|
|
|
if (Enum != NULL) {
|
|
MIDL_user_free(Enum);
|
|
}
|
|
|
|
*ReturnEnum = NULL;
|
|
return(Status);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiAddResourceNode(
|
|
IN HRES_RPC hResource,
|
|
IN HNODE_RPC hNode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a node to the list of nodes where the specified resource
|
|
can be brought online.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource whose list of possible nodes is
|
|
to be modified.
|
|
|
|
hNode - Supplies the node to be added to the resource's list.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
PNM_NODE Node;
|
|
DWORD Status;
|
|
DWORD dwUserModified;
|
|
API_CHECK_INIT();
|
|
|
|
VALIDATE_NODE(Node, hNode);
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
|
|
//
|
|
// Call the FM to do the real work.
|
|
//
|
|
Status = FmChangeResourceNode(Resource, Node, TRUE);
|
|
if (Status != ERROR_SUCCESS) {
|
|
return(Status);
|
|
}
|
|
//
|
|
// BUGBUG: What are the consequences of DmSetValue failing ?
|
|
//
|
|
//write out the fact that the user has explicitly set the
|
|
//resource possible node list
|
|
//
|
|
dwUserModified = 1;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[API] s_ApiAddResourceNode: Setting UserModifiedPossibleNodeList key for resource %1!ws! \r\n",
|
|
OmObjectId(Resource));
|
|
|
|
DmSetValue( Resource->RegistryKey,
|
|
CLUSREG_NAME_RES_USER_MODIFIED_POSSIBLE_LIST,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwUserModified,
|
|
sizeof(DWORD));
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiRemoveResourceNode(
|
|
IN HRES_RPC hResource,
|
|
IN HNODE_RPC hNode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes a node from the list of nodes that can host the
|
|
specified resource. The resource must not be currently
|
|
online on the specified node.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource whose list of possible nodes is
|
|
to be modified.
|
|
|
|
hNode - Supplies the node to be removed from the resource's list.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
PNM_NODE Node;
|
|
DWORD Status;
|
|
DWORD dwUserModified;
|
|
|
|
API_CHECK_INIT();
|
|
|
|
VALIDATE_NODE(Node, hNode);
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
|
|
//
|
|
// Call the FM to do the real work.
|
|
//
|
|
Status = FmChangeResourceNode(Resource, Node, FALSE);
|
|
if (Status != ERROR_SUCCESS) {
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// BUGBUG: What are the consequences of DmSetValue failing.
|
|
//
|
|
//write out the fact that the user has explicitly set the
|
|
//resource possible node list
|
|
//
|
|
dwUserModified = 1;
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[API] s_ApiRemoveResourceNode: Setting UserModifiedPossibleNodeList key for resource %1!ws! \r\n",
|
|
OmObjectId(Resource));
|
|
|
|
DmSetValue( Resource->RegistryKey,
|
|
CLUSREG_NAME_RES_USER_MODIFIED_POSSIBLE_LIST,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwUserModified,
|
|
sizeof(DWORD));
|
|
|
|
//SS: moved the write to the registry settings to the fm
|
|
// layer as well, this way it is truly transactional
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiCreateResourceType(
|
|
IN handle_t IDL_handle,
|
|
IN LPCWSTR lpszTypeName,
|
|
IN LPCWSTR lpszDisplayName,
|
|
IN LPCWSTR lpszDllName,
|
|
IN DWORD dwLooksAlive,
|
|
IN DWORD dwIsAlive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a new resource type in the cluster. Note that this API only
|
|
defines the resource type in the cluster registry and registers the
|
|
resource type with the cluster service. The calling program is
|
|
responsible for installing the resource type DLL on each node in the
|
|
cluster.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - RPC binding handle, not used.
|
|
|
|
lpszResourceTypeName - Supplies the new resource type’s name. The
|
|
specified name must be unique within the cluster.
|
|
|
|
lpszDisplayName - Supplies the display name for the new resource
|
|
type. While lpszResourceTypeName should uniquely identify the
|
|
resource type on all clusters, the lpszDisplayName should be
|
|
a localized friendly name for the resource, suitable for displaying
|
|
to administrators
|
|
|
|
lpszResourceTypeDll - Supplies the name of the new resource type’s DLL.
|
|
|
|
dwLooksAlive - Supplies the default LooksAlive poll interval
|
|
for the new resource type in milliseconds.
|
|
|
|
dwIsAlive - Supplies the default IsAlive poll interval for
|
|
the new resource type in milliseconds.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
HDMKEY TypeKey = NULL;
|
|
DWORD Disposition;
|
|
DWORD dwClusterHighestVersion;
|
|
|
|
//
|
|
// Chittur Subbaraman (chitturs) - 2/8/2000
|
|
//
|
|
// If we are dealing with the mixed mode cluster, do the
|
|
// registry updates right here since the GUM handler won't do it.
|
|
//
|
|
NmGetClusterOperationalVersion( &dwClusterHighestVersion,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
|
|
NT51_MAJOR_VERSION ) {
|
|
//
|
|
// Add the resource information to the registry. If the key does not already
|
|
// exist, then the name is unique and we can go ahead and call the FM to
|
|
// create the actual resource type object.
|
|
//
|
|
TypeKey = DmCreateKey(DmResourceTypesKey,
|
|
lpszTypeName,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
&Disposition);
|
|
if (TypeKey == NULL) {
|
|
return(GetLastError());
|
|
}
|
|
if (Disposition != REG_CREATED_NEW_KEY) {
|
|
DmCloseKey(TypeKey);
|
|
return(ERROR_ALREADY_EXISTS);
|
|
}
|
|
|
|
Status = DmSetValue(TypeKey,
|
|
CLUSREG_NAME_RESTYPE_DLL_NAME,
|
|
REG_SZ,
|
|
(CONST BYTE *)lpszDllName,
|
|
(lstrlenW(lpszDllName)+1)*sizeof(WCHAR));
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto error_exit;
|
|
}
|
|
Status = DmSetValue(TypeKey,
|
|
CLUSREG_NAME_RESTYPE_IS_ALIVE,
|
|
REG_DWORD,
|
|
(CONST BYTE *)&dwIsAlive,
|
|
sizeof(dwIsAlive));
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto error_exit;
|
|
}
|
|
Status = DmSetValue(TypeKey,
|
|
CLUSREG_NAME_RESTYPE_LOOKS_ALIVE,
|
|
REG_DWORD,
|
|
(CONST BYTE *)&dwLooksAlive,
|
|
sizeof(dwIsAlive));
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto error_exit;
|
|
}
|
|
Status = DmSetValue(TypeKey,
|
|
CLUSREG_NAME_RESTYPE_NAME,
|
|
REG_SZ,
|
|
(CONST BYTE *)lpszDisplayName,
|
|
(lstrlenW(lpszDisplayName)+1)*sizeof(WCHAR));
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
Status = FmCreateResourceType(lpszTypeName,
|
|
lpszDisplayName,
|
|
lpszDllName,
|
|
dwLooksAlive,
|
|
dwIsAlive);
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto error_exit;
|
|
}
|
|
|
|
if (TypeKey != NULL) {
|
|
DmCloseKey(TypeKey);
|
|
}
|
|
return(ERROR_SUCCESS);
|
|
|
|
error_exit:
|
|
if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
|
|
NT51_MAJOR_VERSION ) {
|
|
DmCloseKey(TypeKey);
|
|
DmDeleteKey(DmResourceTypesKey, lpszTypeName);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiDeleteResourceType(
|
|
IN handle_t IDL_handle,
|
|
IN LPCWSTR lpszTypeName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes a resource type in the cluster. Note that this API only
|
|
deletes the resource type in the cluster registry and unregisters the
|
|
resource type with the cluster service. The calling program is
|
|
responsible for deleting the resource type DLL on each node in the
|
|
cluster. If any resources of the specified type exist, this API
|
|
fails. The calling program is responsible for deleting any resources
|
|
of this type before deleting the resource type.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - RPC binding handle, not used.
|
|
|
|
lpszResourceTypeName - Supplies the name of the resource type to
|
|
be deleted.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
|
|
//
|
|
// Delete the resource from the FM. This will check to make sure no
|
|
// resources of the specified type exist and check that the resource
|
|
// is already installed.
|
|
//
|
|
Status = FmDeleteResourceType(lpszTypeName);
|
|
if (Status != ERROR_SUCCESS) {
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Now remove the resource type from the registry.
|
|
//
|
|
DmDeleteTree(DmResourceTypesKey, lpszTypeName);
|
|
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_ApiChangeResourceGroup(
|
|
IN HRES_RPC hResource,
|
|
IN HGROUP_RPC hGroup
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Moves a resource from one group to another.
|
|
|
|
Arguments:
|
|
|
|
hResource - Supplies the resource to move.
|
|
|
|
hGroup - Supplies the new group that the resource should be in.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFM_RESOURCE Resource;
|
|
PFM_GROUP Group;
|
|
DWORD Status;
|
|
|
|
API_CHECK_INIT();
|
|
|
|
VALIDATE_RESOURCE_EXISTS(Resource, hResource);
|
|
VALIDATE_GROUP_EXISTS(Group, hGroup);
|
|
|
|
|
|
//
|
|
// Call the FM to do the real work.
|
|
//
|
|
Status = FmChangeResourceGroup(Resource, Group);
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto FnExit;
|
|
}
|
|
|
|
FnExit:
|
|
return(Status);
|
|
}
|
|
|
|
/****
|
|
@func error_status_t | s_ApiCreateResTypeEnum | Enumerates the list of
|
|
nodes in which the resource type can be supported and
|
|
returns the list of nodes to the caller. The client-side
|
|
is responsible for freeing the allocated memory.
|
|
|
|
@parm IN handle_t | IDL_handle | RPC binding handle, not used.
|
|
@parm IN LPCWSTR | lpszTypeName | Name of the resource type.
|
|
@parm IN DWORD | dwType | Supplies the type of properties
|
|
to be enumerated.
|
|
@parm OUT PNM_NODE | ReturnEnum | Returns the requested objects.
|
|
|
|
@comm This routine helps enumerating all the nodes that a particular
|
|
resource type can be supported on.
|
|
|
|
@rdesc ERROR_SUCCESS on success. Win32 error code otherwise.
|
|
|
|
@xref
|
|
****/
|
|
error_status_t
|
|
s_ApiCreateResTypeEnum(
|
|
IN handle_t IDL_handle,
|
|
IN LPCWSTR lpszTypeName,
|
|
IN DWORD dwType,
|
|
OUT PENUM_LIST *ReturnEnum
|
|
)
|
|
{
|
|
DWORD Status;
|
|
DWORD Allocated = 0;
|
|
PENUM_LIST Enum = NULL;
|
|
DWORD i;
|
|
DWORD Result;
|
|
PFM_RESTYPE pResType = NULL;
|
|
PNM_NODE pNode;
|
|
LPWSTR RealName = NULL;
|
|
|
|
pResType = OmReferenceObjectById(ObjectTypeResType,
|
|
lpszTypeName);
|
|
|
|
if (dwType & ~CLUSTER_RESOURCE_TYPE_ENUM_ALL) {
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Allocated = INITIAL_ENUM_LIST_ALLOCATION;
|
|
Enum = MIDL_user_allocate(ENUM_SIZE(Allocated));
|
|
if (Enum == NULL) {
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (pResType == NULL) {
|
|
//
|
|
// The object cannot be found in the list !
|
|
//
|
|
Status = ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Enum->EntryCount = 0;
|
|
|
|
//
|
|
// Enumerate all possible nodes
|
|
//
|
|
if (dwType & CLUSTER_RESOURCE_TYPE_ENUM_NODES) {
|
|
i=0;
|
|
do {
|
|
Result = FmEnumResourceTypeNode(pResType,
|
|
i,
|
|
&pNode);
|
|
if (Result == ERROR_SUCCESS) {
|
|
RealName = (LPWSTR)OmObjectName( pNode );
|
|
if (RealName != NULL) {
|
|
ApipAddToEnum(&Enum,
|
|
&Allocated,
|
|
RealName,
|
|
CLUSTER_RESOURCE_TYPE_ENUM_NODES);
|
|
}
|
|
OmDereferenceObject( pNode );
|
|
++i;
|
|
}
|
|
} while ( Result == ERROR_SUCCESS );
|
|
}
|
|
|
|
*ReturnEnum = Enum;
|
|
OmDereferenceObject( pResType );
|
|
return(ERROR_SUCCESS);
|
|
|
|
ErrorExit:
|
|
if (pResType != NULL) {
|
|
OmDereferenceObject( pResType );
|
|
}
|
|
if (Enum != NULL) {
|
|
MIDL_user_free(Enum);
|
|
}
|
|
|
|
*ReturnEnum = NULL;
|
|
return(Status);
|
|
}
|