1777 lines
52 KiB
C
1777 lines
52 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
resmon.c
|
||
|
||
Abstract:
|
||
|
||
Cluster resource manager interface routines for the resource monitor.
|
||
|
||
Author:
|
||
|
||
Rod Gamache (rodga) 17-Apr-1996
|
||
|
||
|
||
Notes:
|
||
|
||
WARNING: All of the routines in this file assume that the resource
|
||
lock is held when they are called.
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#include "fmp.h"
|
||
|
||
#define LOG_MODULE RESMONF
|
||
|
||
//
|
||
// Global Data
|
||
//
|
||
|
||
//
|
||
// Local function prototypes
|
||
//
|
||
|
||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Resource Control Routines (via Resource Monitor)
|
||
//
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
|
||
DWORD
|
||
FmpRmExceptionFilter(
|
||
DWORD ExceptionCode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Exception filter for calls to the Resource Monitor. These calls will
|
||
often raise an exception if the RPC path to the Resource Monitor fails.
|
||
|
||
Arguments:
|
||
|
||
ExceptionCode - the exception to process.
|
||
|
||
Returns:
|
||
|
||
EXCEPTION_EXECUTE_HANDLE if the exception handler should handle this failure
|
||
EXCEPTION_CONTINUE_SEARCH if the exception is a fatal exception and the handler
|
||
should not handle it.
|
||
|
||
--*/
|
||
|
||
{
|
||
ClRtlLogPrint(LOG_UNUSUAL,
|
||
"[FM] FmpRmExceptionFilter: Unusual exception %1!u! occurred.\n",
|
||
ExceptionCode);
|
||
return(I_RpcExceptionFilter(ExceptionCode));
|
||
} // FmpRmExceptionFilter
|
||
|
||
|
||
|
||
DWORD
|
||
FmpRmCreateResource(
|
||
PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Add a resource to the list of resources managed by the resource monitor.
|
||
|
||
Arguments:
|
||
|
||
Resource - The resource to add.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status;
|
||
PRESMON monitor;
|
||
LPWSTR debugPrefix;
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmCreateResource: creating resource %1!ws! in %2!ws! resource monitor\n",
|
||
OmObjectId(Resource),
|
||
Resource->Flags & RESOURCE_SEPARATE_MONITOR ?
|
||
L"separate" : L"shared");
|
||
|
||
if (Resource->Flags & RESOURCE_SEPARATE_MONITOR) {
|
||
if ( Resource->DebugPrefix != NULL ) {
|
||
debugPrefix = Resource->DebugPrefix;
|
||
} else {
|
||
debugPrefix = Resource->Type->DebugPrefix;
|
||
}
|
||
Resource->Monitor = FmpCreateMonitor(debugPrefix, TRUE);
|
||
if (Resource->Monitor == NULL) {
|
||
return(GetLastError());
|
||
}
|
||
} else {
|
||
CL_ASSERT(FmpDefaultMonitor != NULL);
|
||
Resource->Monitor = FmpDefaultMonitor;
|
||
}
|
||
|
||
try {
|
||
Resource->Id = RmCreateResource(Resource->Monitor->Binding,
|
||
Resource->Type->DllName,
|
||
OmObjectId(Resource->Type),
|
||
OmObjectId(Resource),
|
||
Resource->LooksAlivePollInterval,
|
||
Resource->IsAlivePollInterval,
|
||
(RM_NOTIFY_KEY)Resource,
|
||
Resource->PendingTimeout,
|
||
&status);
|
||
}
|
||
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
||
|
||
DWORD code = GetExceptionCode();
|
||
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] RmCreateResource issued exception %1!u!\n", code);
|
||
|
||
//
|
||
// Stop this resource monitor if it is a separate resource monitor.
|
||
//
|
||
if (Resource->Flags & RESOURCE_SEPARATE_MONITOR) {
|
||
monitor = Resource->Monitor;
|
||
#if 0
|
||
CL_ASSERT( monitor->NotifyThread != NULL );
|
||
CL_ASSERT( monitor->Process != NULL );
|
||
|
||
// Terminate Thread call removed: ( monitor->NotifyThread, 1 );
|
||
CloseHandle( monitor->NotifyThread );
|
||
|
||
TerminateProcess( monitor->Process, 1 );
|
||
LocalFree( monitor );
|
||
#endif
|
||
FmpShutdownMonitor( monitor );
|
||
}
|
||
|
||
Resource->Monitor = NULL;
|
||
return(code);
|
||
}
|
||
|
||
if (Resource->Id != 0) {
|
||
Resource->Flags |= RESOURCE_CREATED;
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmCreateResource: created resource %1!ws!, resid %2!u!\n",
|
||
OmObjectId(Resource),
|
||
Resource->Id);
|
||
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmCreateResource: unable to create resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
//
|
||
// Stop this resource monitor if it is a separate resource monitor.
|
||
//
|
||
if (Resource->Flags & RESOURCE_SEPARATE_MONITOR) {
|
||
monitor = Resource->Monitor;
|
||
#if 0
|
||
CL_ASSERT( monitor->NotifyThread != NULL );
|
||
CL_ASSERT( monitor->Process != NULL );
|
||
|
||
// Terminate Thread call removed: ( monitor->NotifyThread, 1 );
|
||
CloseHandle( monitor->NotifyThread );
|
||
|
||
TerminateProcess( monitor->Process, 1 );
|
||
LocalFree( monitor );
|
||
#endif
|
||
FmpShutdownMonitor( monitor );
|
||
}
|
||
|
||
Resource->Monitor = NULL;
|
||
return(status);
|
||
|
||
} // FmpRmCreateResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpRmOnlineResource(
|
||
PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine requests the Resource Monitor to bring a resource online.
|
||
|
||
Arguments:
|
||
|
||
Resource - A pointer to the resource to bring online.
|
||
|
||
Comments :
|
||
If this is the quorum resource, the exclusive quorum lock should be
|
||
held when this routine is called. Else the quorum lock should be held
|
||
in shared mode. This routine release the lock.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS - if the request was successful.
|
||
ERROR_IO_PENDING - if the request is pending.
|
||
A Win32 error if the request failed.
|
||
|
||
--*/
|
||
|
||
{
|
||
CLUSTER_RESOURCE_STATE state;
|
||
DWORD Status=ERROR_SUCCESS;
|
||
DWORD retry = 200;
|
||
|
||
#if 0
|
||
PVOID callersAddress;
|
||
PVOID callersCaller;
|
||
|
||
RtlGetCallersAddress(
|
||
&callersAddress,
|
||
&callersCaller );
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] RmOnlineResource for <%1!ws!> called from %2!lx! and %3!lx!\n",
|
||
OmObjectId( Resource ),
|
||
callersAddress, callersCaller );
|
||
#endif
|
||
if ( Resource->State > ClusterResourcePending ) {
|
||
Status = ERROR_IO_PENDING;
|
||
return(Status);
|
||
}
|
||
|
||
if ( Resource->State == ClusterResourceOnline ) {
|
||
Status = ERROR_SUCCESS;
|
||
return(Status);
|
||
}
|
||
|
||
|
||
CL_ASSERT((Resource->State == ClusterResourceOffline) ||
|
||
(Resource->State == ClusterResourceFailed));
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOnlineResource: bringing resource %1!ws! (resid %2!u!) online.\n",
|
||
OmObjectId(Resource),
|
||
Resource->Id);
|
||
|
||
//if this is the quorum resource acquire the quolock
|
||
// For registry replication to work, the resource should
|
||
// not be brought online while the quorum resource is offline
|
||
// what do we do for fixquorum mode
|
||
|
||
OmNotifyCb(Resource, NOTIFY_RESOURCE_PREONLINE);
|
||
|
||
//SS:initialize state so that in case of a failure, a failed state is
|
||
// propagated.
|
||
state = ClusterResourceFailed;
|
||
|
||
CheckQuorumState:
|
||
|
||
//CL_ASSERT( (LONG)gdwQuoBlockingResources >= 0 );
|
||
|
||
|
||
if (Resource->QuorumResource) {
|
||
ACQUIRE_EXCLUSIVE_LOCK(gQuoLock);
|
||
|
||
} else {
|
||
DWORD dwOldBlockingFlag;
|
||
|
||
ACQUIRE_SHARED_LOCK(gQuoLock);
|
||
|
||
// if it is not the quorum resource,
|
||
// check the state of the quorum resource
|
||
|
||
// check if the quorum resource is failed
|
||
// we must exit from here and let the recovery for the
|
||
// quorum resource to kick in
|
||
if (gpQuoResource->State == ClusterResourceFailed)
|
||
{
|
||
Status = ERROR_QUORUM_RESOURCE_ONLINE_FAILED;
|
||
CL_LOGFAILURE(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
||
//we dont halt, we will try online again at a later time
|
||
FmpCallResourceNotifyCb(Resource, state);
|
||
FmpPropagateResourceState( Resource, state );
|
||
goto FnExit;
|
||
|
||
}
|
||
|
||
// check if the quorum resource is online,
|
||
// if the quorum resource is marked as waiting and offlinepending,
|
||
// it is actually online
|
||
// if the quorum resource still needs to come online
|
||
// release the lock and wait
|
||
if (((gpQuoResource->State != ClusterResourceOnline) &&
|
||
((gpQuoResource->State != ClusterResourceOfflinePending) ||
|
||
(!(gpQuoResource->Flags & RESOURCE_WAITING))))
|
||
&& !CsNoQuorum)
|
||
{
|
||
// we release the lock here since the quorum resource
|
||
// state transition from pending needs to acquire the lock
|
||
// In general it is a bad idea to do a wait holding locks
|
||
RELEASE_LOCK(gQuoLock);
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOnlineResource: release quolock/group lock and wait on ghQuoOnlineEvent\r\n");
|
||
Status = WaitForSingleObject(ghQuoOnlineEvent, 500);
|
||
if ( Status == WAIT_OBJECT_0 ) {
|
||
// If we're going to retry - make sure we wait a little.
|
||
Sleep( 500 );
|
||
}
|
||
if ( retry-- ) {
|
||
goto CheckQuorumState;
|
||
}
|
||
#if DBG
|
||
if ( IsDebuggerPresent() ) {
|
||
DbgBreakPoint();
|
||
}
|
||
#endif
|
||
CL_LOGFAILURE(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
||
//we dont halt, we will try online again at a later time
|
||
FmpCallResourceNotifyCb(Resource, state);
|
||
FmpPropagateResourceState( Resource, state );
|
||
return(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
||
//CsInconsistencyHalt(ERROR_INVALID_STATE);
|
||
}
|
||
|
||
//
|
||
// assume that we'll be pending... mark the resource as having
|
||
// bumped the QuoBlockResource count.
|
||
//
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOnlineResource: called InterlockedIncrement on gdwQuoBlockingResources for resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
|
||
dwOldBlockingFlag = InterlockedExchange( &Resource->BlockingQuorum, 1 );
|
||
CL_ASSERT( dwOldBlockingFlag == 0 );
|
||
|
||
InterlockedIncrement(&gdwQuoBlockingResources);
|
||
|
||
//
|
||
// everything is now fine on the local node... if any other
|
||
// component (CP) needs to synchronize with the quorum resource, then
|
||
// should acquire the shared lock on the quorum node as part of
|
||
// their operation. If that fails, then they should assume the quorum
|
||
// resource moved, and they should retry.
|
||
//
|
||
}
|
||
|
||
// By now we have either the shared or the exclusive lock on the
|
||
// quorum resource.
|
||
// If we have the shared lock then the quorum resource is online(somewhere).
|
||
// Unlesss there is a failure, it should not go offline.
|
||
|
||
|
||
|
||
Status = ERROR_SUCCESS;
|
||
if (Resource->QuorumResource) {
|
||
Status = FmpRmArbitrateResource( Resource );
|
||
}
|
||
if (Status == ERROR_SUCCESS) {
|
||
Status = RmOnlineResource( Resource->Id,
|
||
(LPDWORD)&state // cast to quiet win64 warning
|
||
);
|
||
if (Resource->QuorumResource && Status != ERROR_SUCCESS) {
|
||
MMSetQuorumOwner( MM_INVALID_NODE , /* Block = */ FALSE, NULL );
|
||
}
|
||
}
|
||
|
||
FmpCallResourceNotifyCb(Resource, state);
|
||
|
||
//SS: the synchronous state propagation must happen when it goes offline
|
||
FmpPropagateResourceState( Resource, state );
|
||
|
||
//
|
||
// Cleanup for the non-quorum resource case.
|
||
//
|
||
if ( !Resource->QuorumResource &&
|
||
Resource->State < ClusterResourcePending ) {
|
||
DWORD dwOldBlockingFlag;
|
||
|
||
dwOldBlockingFlag = InterlockedExchange( &Resource->BlockingQuorum, 0 );
|
||
if ( dwOldBlockingFlag ) {
|
||
//
|
||
// If the Transition Thread processed the request, then we can't
|
||
// perform the decrement.
|
||
//
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOnlineResource: InterlockedDecrement on gdwQuoBlockingResources for resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
|
||
InterlockedDecrement(&gdwQuoBlockingResources);
|
||
}
|
||
}
|
||
|
||
if (Status != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOnlineResource: RmOnlineResource Failed. Resource %1!ws!, status %2!u!\n",
|
||
OmObjectId(Resource),
|
||
Status);
|
||
}
|
||
|
||
//if RmOnlineResource is successful, do the post processing
|
||
if ( Resource->State == ClusterResourceOnline ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOnlineResource: %1!ws! is now online\n",
|
||
OmObjectId(Resource));
|
||
//if this is the quorum resource and it goes into online state
|
||
//immediately, wake other threads
|
||
if (Resource->QuorumResource)
|
||
SetEvent(ghQuoOnlineEvent);
|
||
|
||
} else if ( Resource->State > ClusterResourcePending ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOnlineResource: Resource %1!ws! pending\n",
|
||
OmObjectId(Resource));
|
||
//SS: what should we tell the callbacks
|
||
//FmpNotifyResourceCb(Resource,??);
|
||
//will they eventually get called if so how ?
|
||
if (Resource->QuorumResource)
|
||
{
|
||
//the quorum resource is coming online, unsignal the event so that
|
||
//all threads that need quorum resource to be online will block
|
||
ResetEvent(ghQuoOnlineEvent);
|
||
}
|
||
Status = ERROR_IO_PENDING;
|
||
} else {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOnlineResource: Failed. Resource %1!ws!, state %2!u!\n",
|
||
OmObjectId(Resource),
|
||
Resource->State);
|
||
//
|
||
// rjain: for a synchronous resource we must post RESOURCE_FAILED event
|
||
// so thatfailback policies are correctly followed
|
||
// Also pretend that the old state to be online to actually force the
|
||
// restart behaviour. See: FmpProcessResourceEvents.
|
||
//
|
||
OmReferenceObject(Resource);
|
||
FmpPostWorkItem(FM_EVENT_RES_RESOURCE_FAILED,
|
||
Resource,
|
||
ClusterResourceOnline);
|
||
Status = ERROR_RESMON_ONLINE_FAILED;
|
||
}
|
||
|
||
FnExit:
|
||
RELEASE_LOCK(gQuoLock);
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOnlineResource: Returning. Resource %1!ws!, state %2!u!, status %3!u!.\n",
|
||
OmObjectId(Resource),
|
||
Resource->State,
|
||
Status);
|
||
return (Status);
|
||
|
||
} // FmpRmOnlineResource
|
||
|
||
|
||
|
||
VOID
|
||
FmpRmTerminateResource(
|
||
PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Terminates (immediately) a resource.
|
||
|
||
Arguments:
|
||
|
||
Resource - A pointer to the resource to terminate.
|
||
|
||
Returns:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwOldBlockingFlag;
|
||
|
||
|
||
//notify callbacks that need preprocessing before a resource is
|
||
//brought offline-call this here since all resources may not go
|
||
//thru the offline pending transition
|
||
//SS - what if the resource never even goes to offline
|
||
//pending state - then should we renotify the callbacks that it
|
||
//is still online?
|
||
OmNotifyCb(Resource, NOTIFY_RESOURCE_PREOFFLINE);
|
||
|
||
//
|
||
// Try to terminate the resource.
|
||
//
|
||
try {
|
||
if (Resource->QuorumResource) {
|
||
MMSetQuorumOwner( MM_INVALID_NODE, /* Block = */ FALSE, NULL );
|
||
}
|
||
RmTerminateResource(Resource->Id);
|
||
|
||
// if FmpRmterminate was called for a failed resource, mark
|
||
// the resource as Failed and not Offline.
|
||
if (Resource->State == ClusterResourceFailed)
|
||
{
|
||
FmpCallResourceNotifyCb(Resource, ClusterResourceFailed);
|
||
FmpPropagateResourceState( Resource, ClusterResourceFailed );
|
||
}
|
||
|
||
else
|
||
{
|
||
FmpCallResourceNotifyCb(Resource, ClusterResourceOffline);
|
||
FmpPropagateResourceState( Resource, ClusterResourceOffline );
|
||
}
|
||
}
|
||
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
||
|
||
DWORD code = GetExceptionCode();
|
||
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] RmTerminateResource issued exception %1!u!\n", code);
|
||
|
||
return;
|
||
}
|
||
|
||
//if terminate was called during a pending state, this resource may be
|
||
//blocking the quorum resource, decrement the blocking count
|
||
dwOldBlockingFlag = InterlockedExchange( &Resource->BlockingQuorum, 0 );
|
||
|
||
if ( dwOldBlockingFlag ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmTerminateResource: InterlockedDecrement on gdwQuoBlockingResources, Resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
InterlockedDecrement(&gdwQuoBlockingResources);
|
||
}
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] RmTerminateResource: %1!ws! is now offline\n",
|
||
OmObjectId(Resource));
|
||
|
||
return;
|
||
|
||
} // FmpRmTerminateResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpRmOfflineResource(
|
||
PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Calls the Resource Monitor to take a resource offline.
|
||
|
||
Arguments:
|
||
|
||
Resource - A pointer to the resource to terminate.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if the request is successful.
|
||
ERROR_IO_PENDING if the request is pending.
|
||
Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
CLUSTER_RESOURCE_STATE state;
|
||
DWORD status;
|
||
DWORD retry = 200;
|
||
|
||
#if DBG
|
||
PLIST_ENTRY listEntry;
|
||
#endif
|
||
|
||
if ( Resource->State > ClusterResourcePending ) {
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] FmpRmOfflineResource: pending condition\n");
|
||
return(ERROR_IO_PENDING);
|
||
}
|
||
|
||
CL_ASSERT(Resource->State != ClusterResourceOffline);
|
||
|
||
#if DBG
|
||
// everything else in the same group must be offline if this is the
|
||
// quorum resource
|
||
if ( Resource->QuorumResource ) {
|
||
PFM_GROUP group = Resource->Group;
|
||
PFM_RESOURCE resource;
|
||
|
||
for ( listEntry = group->Contains.Flink;
|
||
listEntry != &(group->Contains);
|
||
listEntry = listEntry->Flink ) {
|
||
|
||
resource = CONTAINING_RECORD(listEntry,
|
||
FM_RESOURCE,
|
||
ContainsLinkage );
|
||
if ( (Resource != resource) &&
|
||
(resource->State != ClusterResourceOffline) &&
|
||
(resource->State != ClusterResourceFailed) &&
|
||
(resource->State != ClusterResourceOfflinePending) ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] RmOfflineResource: resource <%1!ws!> not offline when the quorum resource was shutting down.\n",
|
||
OmObjectId(resource));
|
||
CsInconsistencyHalt(ERROR_INVALID_STATE);
|
||
}
|
||
}
|
||
} else {
|
||
// this is not the quorum resource... but if the quorum resource is in
|
||
// this group, it must not be offline!
|
||
PFM_GROUP group = Resource->Group;
|
||
PFM_RESOURCE resource;
|
||
|
||
for ( listEntry = group->Contains.Flink;
|
||
listEntry != &(group->Contains);
|
||
listEntry = listEntry->Flink ) {
|
||
|
||
resource = CONTAINING_RECORD(listEntry,
|
||
FM_RESOURCE,
|
||
ContainsLinkage );
|
||
if ( (resource->QuorumResource) &&
|
||
((resource->State == ClusterResourceOffline) ||
|
||
(resource->State == ClusterResourceFailed) ||
|
||
((resource->State == ClusterResourceOfflinePending) &&
|
||
(!resource->Flags & RESOURCE_WAITING))) ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] RmOfflineResource: quorum resource <%1!ws!> offline when resource <%2!ws!> was shutting down.\n",
|
||
OmObjectId(resource),
|
||
OmObjectId(Resource));
|
||
CsInconsistencyHalt(ERROR_INVALID_STATE);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
state = ClusterResourceFailed;
|
||
|
||
CheckQuorumState:
|
||
|
||
//if this is the quorum resource acquire the quolock
|
||
// For registry replication to work, the resource should
|
||
// not be brought online while the quorum resource is offline
|
||
// what do we do for fixquorum mode
|
||
if (Resource->QuorumResource) {
|
||
ACQUIRE_EXCLUSIVE_LOCK(gQuoLock);
|
||
} else {
|
||
ACQUIRE_SHARED_LOCK(gQuoLock);
|
||
}
|
||
|
||
//if it is not the quorum resource, check the state of the quorum resource
|
||
if (!Resource->QuorumResource)
|
||
{
|
||
DWORD dwOldBlockingFlag;
|
||
|
||
// check if the quorum resource is failed
|
||
// we must exit from here and allow the recovery for the
|
||
// quorum resource to kick in, which can only happen when
|
||
// the group lock is released
|
||
if (gpQuoResource->State == ClusterResourceFailed)
|
||
{
|
||
status = ERROR_QUORUM_RESOURCE_ONLINE_FAILED;
|
||
CL_LOGFAILURE(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
||
RELEASE_LOCK(gQuoLock);
|
||
FmpCallResourceNotifyCb(Resource, state);
|
||
FmpPropagateResourceState( Resource, state );
|
||
return(status);
|
||
|
||
}
|
||
|
||
// check if the quorum resource is online,
|
||
// if the quorum resource is marked as waiting and offlinepending,
|
||
// it is actually online.
|
||
// if the quorum resource still needs to come online,
|
||
// release the lock and wait
|
||
if (((gpQuoResource->State != ClusterResourceOnline) &&
|
||
((gpQuoResource->State != ClusterResourceOfflinePending) ||
|
||
(!(gpQuoResource->Flags & RESOURCE_WAITING))))
|
||
&& !CsNoQuorum)
|
||
{
|
||
RELEASE_LOCK(gQuoLock);
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOfflineResource: release quolock/group lock and wait on ghQuoOnlineEvent\r\n");
|
||
WaitForSingleObject(ghQuoOnlineEvent, 500);
|
||
if ( retry-- ) {
|
||
Sleep(500);
|
||
goto CheckQuorumState;
|
||
}
|
||
#if DBG
|
||
if ( IsDebuggerPresent() ) {
|
||
DbgBreakPoint();
|
||
}
|
||
#endif
|
||
CL_LOGFAILURE(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
||
// Should we halt? What about the pre-online notification above?
|
||
FmpCallResourceNotifyCb(Resource, state);
|
||
FmpPropagateResourceState( Resource, state );
|
||
return(ERROR_QUORUM_RESOURCE_ONLINE_FAILED);
|
||
//CsInconsistencyHalt(ERROR_INVALID_STATE);
|
||
|
||
}
|
||
|
||
//
|
||
// assume that we'll be pending... mark the resource as having
|
||
// bumped the QuoBlockResource count.
|
||
//
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOfflineResource: InterlockedIncrement on gdwQuoBlockingResources for resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
|
||
dwOldBlockingFlag = InterlockedExchange( &Resource->BlockingQuorum, 1 );
|
||
CL_ASSERT( dwOldBlockingFlag == 0 );
|
||
|
||
InterlockedIncrement(&gdwQuoBlockingResources);
|
||
|
||
}
|
||
else
|
||
{
|
||
DWORD dwNumBlockingResources;
|
||
|
||
//allow resources about 30 seconds to finish a pending
|
||
//operation
|
||
retry = 60;
|
||
|
||
// This is for a quorum resource.
|
||
|
||
CheckPendingResources:
|
||
|
||
// this is the quorum resource, wait for other resources
|
||
// to get out of their pending states
|
||
// new resources are not allowed to queue since the quorum
|
||
// lock is held exclusively
|
||
|
||
dwNumBlockingResources =
|
||
InterlockedCompareExchange( &gdwQuoBlockingResources, 0, 0 );
|
||
|
||
if (dwNumBlockingResources)
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOfflineResource: Quorum resource waiting to be brought offline-sleep.BlckingRes=%1!u!\r\n",
|
||
dwNumBlockingResources);
|
||
//sleep for 500 msec
|
||
Sleep(500);
|
||
if ( retry-- ) {
|
||
goto CheckPendingResources;
|
||
}
|
||
//if some resources are still pending, go ahead and offline
|
||
//the quorum, the checkpointing code will simply retry when
|
||
//it finds that the quorum resource is not available
|
||
#if 0
|
||
if ( IsDebuggerPresent() ) {
|
||
DbgBreakPoint();
|
||
}
|
||
#endif
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOfflineResource: Quorum resource is being brought offline despite %1!u! pending resources...\r\n",
|
||
dwNumBlockingResources);
|
||
|
||
}
|
||
}
|
||
|
||
status = ERROR_SUCCESS;
|
||
if (Resource->QuorumResource) {
|
||
state = Resource->State;
|
||
status = MMSetQuorumOwner( MM_INVALID_NODE, /* Block = */ TRUE, NULL );
|
||
}
|
||
|
||
if (status == ERROR_SUCCESS) {
|
||
//notify callbacks that need preprocessing before a resource is
|
||
//brought offline-call this here since all resources may not go
|
||
//thru the offline pending transition
|
||
//SS - what if the resource never even goes to offline
|
||
//pending state - then should we renotify the callbacks that it
|
||
//is still online?
|
||
state = ClusterResourceOffline;
|
||
|
||
OmNotifyCb(Resource, NOTIFY_RESOURCE_PREOFFLINE);
|
||
status = RmOfflineResource( Resource->Id,
|
||
(LPDWORD)&state // cast to quiet win64 warning
|
||
);
|
||
}
|
||
|
||
//
|
||
// Cleanup for the non-quorum resource case
|
||
// if the resource has gone offline, decrement the count
|
||
//
|
||
if ( !Resource->QuorumResource &&
|
||
state < ClusterResourcePending ) {
|
||
DWORD dwOldBlockingFlag;
|
||
|
||
dwOldBlockingFlag = InterlockedExchange( &Resource->BlockingQuorum, 0 );
|
||
if ( dwOldBlockingFlag ) {
|
||
//
|
||
// If the Transition Thread processed the request, then we can't
|
||
// perform the decrement.
|
||
//
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOfflineResource: InterlockedDecrement on gdwQuoBlockingResources for resource %1!ws!\n",
|
||
OmObjectId(Resource));
|
||
|
||
InterlockedDecrement(&gdwQuoBlockingResources);
|
||
}
|
||
}
|
||
|
||
if (status == ERROR_SUCCESS)
|
||
{
|
||
//
|
||
// If the new state is pending, then we must wait.
|
||
//
|
||
if ( state == ClusterResourceOffline ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOfflineResource: %1!ws! is now offline\n",
|
||
OmObjectId(Resource));
|
||
} else if ( state == ClusterResourceOfflinePending ) {
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOfflineResource: %1!ws! offline pending\n",
|
||
OmObjectId(Resource));
|
||
status = ERROR_IO_PENDING;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmOfflineResource: RmOffline() for %1!ws! returned error %2!u!\r\n",
|
||
OmObjectId(Resource), status);
|
||
}
|
||
|
||
FmpCallResourceNotifyCb(Resource, state);
|
||
FmpPropagateResourceState( Resource, state );
|
||
RELEASE_LOCK(gQuoLock);
|
||
|
||
return(status);
|
||
|
||
} // FmpRmOfflineResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpRmCloseResource(
|
||
PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes a resource from those being managed by the resource monitor.
|
||
|
||
Arguments:
|
||
|
||
Resource - The resource to remove.
|
||
|
||
Returns:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status;
|
||
PRESMON monitor;
|
||
|
||
if (Resource->Id == 0) {
|
||
//
|
||
// This resource was never fully created.
|
||
//
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
monitor = Resource->Monitor;
|
||
Resource->Monitor = NULL;
|
||
|
||
if ( Resource->QuorumResource ) {
|
||
RmReleaseResource( Resource->Id );
|
||
Resource->QuorumResource = FALSE;
|
||
}
|
||
|
||
try {
|
||
RmCloseResource(&Resource->Id);
|
||
}
|
||
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
||
|
||
DWORD status = GetExceptionCode();
|
||
|
||
ClRtlLogPrint(LOG_NOISE,"[FM] RmDestroyResource issued exception %1!u!\n", status);
|
||
|
||
}
|
||
|
||
if ( monitor &&
|
||
Resource->Flags & RESOURCE_SEPARATE_MONITOR) {
|
||
//
|
||
// Shutdown the resource monitor as well.
|
||
//
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] Shutting down separate resource monitor!\n");
|
||
FmpShutdownMonitor(monitor);
|
||
}
|
||
|
||
Resource->Id = 0;
|
||
|
||
return(ERROR_SUCCESS);
|
||
|
||
} // FmpRmCloseResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpRmArbitrateResource(
|
||
IN PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arbitrate for the given resource.
|
||
|
||
Arguments:
|
||
|
||
Resource - The resource to arbitrate.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status = ERROR_SUCCESS;
|
||
|
||
if (Resource->Id == 0) {
|
||
//
|
||
// This resource was never fully created.
|
||
//
|
||
return(ERROR_RESOURCE_NOT_AVAILABLE);
|
||
}
|
||
try {
|
||
if (Resource->QuorumResource) {
|
||
status = MMSetQuorumOwner( NmGetNodeId(NmLocalNode), /* Block = */ TRUE, NULL );
|
||
}
|
||
if (status == ERROR_SUCCESS) {
|
||
status = RmArbitrateResource(Resource->Id);
|
||
if (status != ERROR_SUCCESS) {
|
||
if (Resource->QuorumResource) {
|
||
MMSetQuorumOwner( MM_INVALID_NODE , /* Block = */ FALSE, NULL );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
||
|
||
DWORD status = GetExceptionCode();
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] RmArbitrateResource issued exception %1!u!\n",
|
||
status);
|
||
|
||
}
|
||
|
||
return(status);
|
||
|
||
} // FmpRmArbitrateResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpRmReleaseResource(
|
||
IN PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Release arbitration on a given resource.
|
||
|
||
Arguments:
|
||
|
||
Resource - The resource to release.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status = ERROR_SUCCESS;
|
||
|
||
if (Resource->Id == 0) {
|
||
//
|
||
// This resource was never fully created.
|
||
//
|
||
return(ERROR_RESOURCE_NOT_AVAILABLE);
|
||
}
|
||
try {
|
||
status = RmReleaseResource(Resource->Id);
|
||
}
|
||
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
||
|
||
DWORD status = GetExceptionCode();
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] RmReleaseResource issued exception %1!u!\n",
|
||
status);
|
||
|
||
}
|
||
|
||
return(status);
|
||
|
||
} // FmpRmReleaseResource
|
||
|
||
|
||
|
||
DWORD
|
||
FmpRmFailResource(
|
||
IN PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Fail a given resource.
|
||
|
||
Arguments:
|
||
|
||
Resource - The resource to fail.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
if (Resource->QuorumResource) {
|
||
MMSetQuorumOwner( MM_INVALID_NODE, /* Block = */ FALSE, NULL );
|
||
}
|
||
return(RmFailResource(Resource->Id));
|
||
|
||
} // FmpRmFailResource
|
||
|
||
DWORD FmpRmLoadResTypeDll(
|
||
IN PFM_RESTYPE pResType
|
||
)
|
||
{
|
||
PRESMON monitor;
|
||
DWORD dwStatus;
|
||
LPWSTR pszDebugPrefix;
|
||
|
||
|
||
// Read the DebugControlFunction registry value.
|
||
//
|
||
|
||
if ( pResType->Flags & RESTYPE_DEBUG_CONTROL_FUNC ) {
|
||
if ( pResType->DebugPrefix != NULL ) {
|
||
pszDebugPrefix = pResType->DebugPrefix;
|
||
} else {
|
||
pszDebugPrefix = pResType->DebugPrefix;
|
||
}
|
||
monitor = FmpCreateMonitor(pszDebugPrefix, TRUE);
|
||
if (monitor == NULL) {
|
||
dwStatus = GetLastError();
|
||
goto FnExit;
|
||
}
|
||
} else {
|
||
CL_ASSERT(FmpDefaultMonitor != NULL);
|
||
monitor = FmpDefaultMonitor;
|
||
}
|
||
|
||
|
||
dwStatus = RmLoadResourceTypeDll(monitor->Binding, OmObjectId(pResType),
|
||
pResType->DllName);
|
||
|
||
|
||
if (dwStatus != ERROR_SUCCESS)
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] RmLoadResourceTypeDll call failed %1!u!\n",
|
||
dwStatus);
|
||
}
|
||
|
||
if ( pResType->Flags & RESTYPE_DEBUG_CONTROL_FUNC )
|
||
{
|
||
//
|
||
// Stop this resource monitor if it is a separate resource monitor.
|
||
//
|
||
CL_ASSERT( monitor->NotifyThread != NULL );
|
||
CL_ASSERT( monitor->Process != NULL );
|
||
|
||
FmpShutdownMonitor( monitor );
|
||
|
||
}
|
||
|
||
|
||
FnExit:
|
||
return(dwStatus);
|
||
|
||
}
|
||
|
||
|
||
|
||
DWORD
|
||
FmpRmChangeResourceParams(
|
||
IN PFM_RESOURCE Resource
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Tell the resource monitor to change parameters for the given resource.
|
||
|
||
Arguments:
|
||
|
||
Resource - The resource to change parameters.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful.
|
||
|
||
A Win32 error code on failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmChangeResourceParams for resource <%1!ws!> called...\n",
|
||
OmObjectId(Resource));
|
||
|
||
return(RmChangeResourceParams(
|
||
Resource->Id,
|
||
Resource->LooksAlivePollInterval,
|
||
Resource->IsAlivePollInterval,
|
||
Resource->PendingTimeout ) );
|
||
|
||
} // FmpRmChangeResourceParams
|
||
|
||
|
||
|
||
DWORD
|
||
FmpRmResourceControl(
|
||
IN PFM_RESOURCE Resource,
|
||
IN DWORD ControlCode,
|
||
IN PUCHAR InBuffer,
|
||
IN DWORD InBufferSize,
|
||
OUT PUCHAR OutBuffer,
|
||
IN DWORD OutBufferSize,
|
||
OUT LPDWORD BytesReturned,
|
||
OUT LPDWORD Required
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Provides for arbitrary communication and control between an application
|
||
and a specific instance of a resource.
|
||
|
||
Arguments:
|
||
|
||
Resource - Supplies the resource to be controlled.
|
||
|
||
ControlCode- Supplies the control code that defines the
|
||
structure and action of the resource control.
|
||
Values of ControlCode between 0 and 0x10000000 are reserved
|
||
for future definition and use by Microsoft. All other values
|
||
are available for use by ISVs
|
||
|
||
InBuffer- Supplies a pointer to the input buffer to be passed
|
||
to the resource.
|
||
|
||
InBufferSize- Supplies the size, in bytes, of the data pointed
|
||
to by lpInBuffer..
|
||
|
||
OutBuffer- Supplies a pointer to the output buffer to be
|
||
filled in by the resource..
|
||
|
||
OutBufferSize- Supplies the size, in bytes, of the available
|
||
space pointed to by lpOutBuffer.
|
||
|
||
BytesReturned - Returns the number of bytes of lpOutBuffer
|
||
actually filled in by the resource..
|
||
|
||
Required - The number of bytes required if OutBuffer is not big enough.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error code otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status;
|
||
DWORD Dummy;
|
||
DWORD dwTmpBytesReturned;
|
||
DWORD dwTmpBytesRequired;
|
||
CLUSPROP_BUFFER_HELPER props;
|
||
DWORD bufSize;
|
||
|
||
CL_ASSERT( Resource->Group != NULL );
|
||
//
|
||
// Handle any requests that must be done without locks helds.
|
||
//
|
||
switch ( ControlCode ) {
|
||
|
||
case CLUSCTL_RESOURCE_GET_NAME:
|
||
if ( (Resource->Monitor == NULL) ||
|
||
(OmObjectName( Resource ) == NULL) ) {
|
||
return(ERROR_NOT_READY);
|
||
}
|
||
props.pb = OutBuffer;
|
||
bufSize = (lstrlenW( OmObjectName( Resource ) ) + 1) * sizeof(WCHAR);
|
||
if ( bufSize > OutBufferSize ) {
|
||
*Required = bufSize;
|
||
*BytesReturned = 0;
|
||
status = ERROR_MORE_DATA;
|
||
} else {
|
||
lstrcpyW( props.psz, OmObjectName( Resource ) );
|
||
*BytesReturned = bufSize;
|
||
*Required = 0;
|
||
status = ERROR_SUCCESS;
|
||
}
|
||
return(status);
|
||
|
||
case CLUSCTL_RESOURCE_GET_ID:
|
||
if ( (Resource->Monitor == NULL) ||
|
||
(OmObjectId( Resource ) == NULL) ) {
|
||
return(ERROR_NOT_READY);
|
||
}
|
||
props.pb = OutBuffer;
|
||
bufSize = (lstrlenW( OmObjectId( Resource ) ) + 1) * sizeof(WCHAR);
|
||
if ( bufSize > OutBufferSize ) {
|
||
*Required = bufSize;
|
||
*BytesReturned = 0;
|
||
status = ERROR_MORE_DATA;
|
||
} else {
|
||
lstrcpyW( props.psz, OmObjectId( Resource ) );
|
||
*BytesReturned = bufSize;
|
||
*Required = 0;
|
||
status = ERROR_SUCCESS;
|
||
}
|
||
return(status);
|
||
|
||
case CLUSCTL_RESOURCE_GET_RESOURCE_TYPE:
|
||
if ( (Resource->Monitor == NULL) ||
|
||
(OmObjectId( Resource->Type ) == NULL) ) {
|
||
return(ERROR_NOT_READY);
|
||
}
|
||
props.pb = OutBuffer;
|
||
bufSize = (lstrlenW( OmObjectId( Resource->Type ) ) + 1) * sizeof(WCHAR);
|
||
if ( bufSize > OutBufferSize ) {
|
||
*Required = bufSize;
|
||
*BytesReturned = 0;
|
||
status = ERROR_MORE_DATA;
|
||
} else {
|
||
lstrcpyW( props.psz, OmObjectId( Resource->Type ) );
|
||
*BytesReturned = bufSize;
|
||
*Required = 0;
|
||
status = ERROR_SUCCESS;
|
||
}
|
||
return(status);
|
||
|
||
case CLUSCTL_RESOURCE_ADD_REGISTRY_CHECKPOINT:
|
||
case CLUSCTL_RESOURCE_DELETE_REGISTRY_CHECKPOINT:
|
||
{
|
||
LPWSTR RegistryKey;
|
||
DWORD LastChar;
|
||
|
||
//
|
||
// Validate the input buffer
|
||
//
|
||
RegistryKey = (LPWSTR)InBuffer;
|
||
LastChar = (InBufferSize/sizeof(WCHAR)) - 1;
|
||
//
|
||
// If the length of the input buffer is zero, or not a integral
|
||
// number of WCHARs, or the last character is not NULL, the
|
||
// request is invalid.
|
||
//
|
||
if ((InBufferSize < sizeof(WCHAR)) ||
|
||
((InBufferSize % sizeof(WCHAR)) != 0) ||
|
||
(RegistryKey == NULL) ||
|
||
(RegistryKey[LastChar] != L'\0')) {
|
||
return(ERROR_INVALID_PARAMETER);
|
||
}
|
||
|
||
//
|
||
// If we are not the owner of this resource, don't let the set
|
||
// happen.
|
||
//
|
||
if (Resource->Group->OwnerNode != NmLocalNode) {
|
||
return(ERROR_HOST_NODE_NOT_RESOURCE_OWNER);
|
||
}
|
||
|
||
//
|
||
// Call the checkpoint manager to perform the change.
|
||
//
|
||
if (ControlCode == CLUSCTL_RESOURCE_ADD_REGISTRY_CHECKPOINT) {
|
||
status = CpAddRegistryCheckpoint(Resource, RegistryKey);
|
||
} else {
|
||
status = CpDeleteRegistryCheckpoint(Resource, RegistryKey);
|
||
}
|
||
}
|
||
*BytesReturned = 0;
|
||
return(status);
|
||
|
||
case CLUSCTL_RESOURCE_ADD_CRYPTO_CHECKPOINT:
|
||
case CLUSCTL_RESOURCE_DELETE_CRYPTO_CHECKPOINT:
|
||
{
|
||
//
|
||
// If we are not the owner of this resource, don't let the set
|
||
// happen.
|
||
//
|
||
if (Resource->Group->OwnerNode != NmLocalNode) {
|
||
return(ERROR_HOST_NODE_NOT_RESOURCE_OWNER);
|
||
}
|
||
|
||
//
|
||
// Call the checkpoint manager to perform the change.
|
||
//
|
||
if (ControlCode == CLUSCTL_RESOURCE_ADD_CRYPTO_CHECKPOINT) {
|
||
status = CpckAddCryptoCheckpoint(Resource, InBuffer, InBufferSize);
|
||
} else {
|
||
status = CpckDeleteCryptoCheckpoint(Resource, InBuffer, InBufferSize);
|
||
}
|
||
}
|
||
*BytesReturned = 0;
|
||
return(status);
|
||
|
||
case CLUSCTL_RESOURCE_GET_REGISTRY_CHECKPOINTS:
|
||
//
|
||
// Call the checkpoint manager to retrieve the list of checkpoints
|
||
//
|
||
status = CpGetRegistryCheckpoints(Resource,
|
||
OutBuffer,
|
||
OutBufferSize,
|
||
BytesReturned,
|
||
Required);
|
||
return(status);
|
||
|
||
case CLUSCTL_RESOURCE_GET_CRYPTO_CHECKPOINTS:
|
||
//
|
||
// Call the checkpoint manager to retrieve the list of checkpoints
|
||
//
|
||
status = CpckGetCryptoCheckpoints(Resource,
|
||
OutBuffer,
|
||
OutBufferSize,
|
||
BytesReturned,
|
||
Required);
|
||
return(status);
|
||
|
||
case CLUSCTL_RESOURCE_UPGRADE_DLL:
|
||
status = FmpUpgradeResourceDLL(Resource,
|
||
( LPWSTR ) InBuffer);
|
||
return(status);
|
||
|
||
default:
|
||
break;
|
||
|
||
}
|
||
|
||
|
||
OmReferenceObject( Resource );
|
||
|
||
FmpAcquireLocalResourceLock( Resource );
|
||
|
||
//if the resource has been marked for delete, then fail this call
|
||
if (!IS_VALID_FM_RESOURCE(Resource))
|
||
{
|
||
status = ERROR_RESOURCE_NOT_AVAILABLE;
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
goto FnExit;
|
||
}
|
||
|
||
if ( Resource->Monitor == NULL ) {
|
||
status = FmpInitializeResource( Resource, TRUE );
|
||
if ( status != ERROR_SUCCESS ) {
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
goto FnExit;
|
||
}
|
||
}
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
|
||
//to take care of the output reference pointer which cannot be NULL.
|
||
if (!OutBuffer)
|
||
{
|
||
OutBuffer = (PUCHAR)&Dummy;
|
||
OutBufferSize = 0;
|
||
}
|
||
if (!BytesReturned)
|
||
BytesReturned = &dwTmpBytesReturned;
|
||
if (!Required)
|
||
Required = &dwTmpBytesRequired;
|
||
|
||
try {
|
||
status = RmResourceControl(Resource->Id,
|
||
ControlCode,
|
||
InBuffer,
|
||
InBufferSize,
|
||
OutBuffer,
|
||
OutBufferSize,
|
||
BytesReturned,
|
||
Required
|
||
);
|
||
}
|
||
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
||
status = GetExceptionCode();
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] RmResourceControl issued exception %1!u!\n",
|
||
status);
|
||
}
|
||
|
||
if ( ( status != ERROR_SUCCESS ) &&
|
||
( status != ERROR_MORE_DATA ) &&
|
||
( status != ERROR_INVALID_FUNCTION ) )
|
||
{
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] FmpRmResourceControl: RmResourceControl returned %1!u! for resource %2!ws!, resid=%3!u!...\n",
|
||
status,
|
||
OmObjectId(Resource),
|
||
Resource->Id);
|
||
}
|
||
|
||
//for core resource we may need special handling
|
||
if ((status == ERROR_SUCCESS) || (status == ERROR_RESOURCE_PROPERTIES_STORED))
|
||
{
|
||
DWORD dwPostProcessStatus;
|
||
|
||
dwPostProcessStatus = FmpPostProcessResourceControl( Resource,
|
||
ControlCode,
|
||
InBuffer,
|
||
InBufferSize,
|
||
OutBuffer,
|
||
OutBufferSize,
|
||
BytesReturned,
|
||
Required );
|
||
if ( dwPostProcessStatus != ERROR_SUCCESS ) status = dwPostProcessStatus;
|
||
}
|
||
|
||
if ( ((status == ERROR_SUCCESS) ||
|
||
(status == ERROR_RESOURCE_PROPERTIES_STORED)) &&
|
||
(ControlCode & CLCTL_MODIFY_MASK) ) {
|
||
|
||
//
|
||
// We cannot just broadcast a cluster wide event... which is what
|
||
// we want to do. Unfortunately, this code path can be activated
|
||
// from within a GUM call, and we cannot call GUM back until we
|
||
// have dispatched the current event.
|
||
//
|
||
|
||
//
|
||
// Reference the resource object to keep it around while we
|
||
// perform the post notification. The dereference must occur
|
||
// in the post routine after the event posting.
|
||
//
|
||
OmReferenceObject( Resource );
|
||
|
||
FmpPostWorkItem( FM_EVENT_RESOURCE_PROPERTY_CHANGE,
|
||
Resource,
|
||
0 );
|
||
}
|
||
|
||
FnExit:
|
||
OmDereferenceObject( Resource );
|
||
//FmpReleaseLocalResourceLock( Resource );
|
||
return(status);
|
||
|
||
} // FmpRmResourceControl
|
||
|
||
|
||
DWORD
|
||
FmpRmResourceTypeControl(
|
||
IN LPCWSTR ResourceTypeName,
|
||
IN DWORD ControlCode,
|
||
IN PUCHAR InBuffer,
|
||
IN DWORD InBufferSize,
|
||
OUT PUCHAR OutBuffer,
|
||
IN DWORD OutBufferSize,
|
||
OUT LPDWORD BytesReturned,
|
||
OUT LPDWORD Required
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Provides for arbitrary communication and control between an application
|
||
and a specific instance of a resource type.
|
||
|
||
Arguments:
|
||
|
||
ResourceTypeName - Supplies the name of the resource type to be
|
||
controlled.
|
||
|
||
ControlCode- Supplies the control code that defines the
|
||
structure and action of the resource control.
|
||
Values of dwControlCode between 0 and 0x10000000 are reserved
|
||
for future definition and use by Microsoft. All other values
|
||
are available for use by ISVs
|
||
|
||
InBuffer- Supplies a pointer to the input buffer to be passed
|
||
to the resource.
|
||
|
||
InBufferSize- Supplies the size, in bytes, of the data pointed
|
||
to by lpInBuffer..
|
||
|
||
OutBuffer- Supplies a pointer to the output buffer to be
|
||
filled in by the resource..
|
||
|
||
OutBufferSize- Supplies the size, in bytes, of the available
|
||
space pointed to by lpOutBuffer.
|
||
|
||
BytesReturned - Returns the number of bytes of lpOutBuffer
|
||
actually filled in by the resource..
|
||
|
||
Required - The number of bytes required if OutBuffer is not big enough.
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS if successful
|
||
|
||
Win32 error code otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD status;
|
||
PRESMON monitor;
|
||
PFM_RESTYPE type = NULL;
|
||
LPWSTR debugPrefix;
|
||
DWORD Dummy;
|
||
DWORD dwTmpBytesReturned;
|
||
DWORD dwTmpBytesRequired;
|
||
|
||
|
||
//
|
||
// Find the resource type structure associated with this resource type name
|
||
//
|
||
OmEnumObjects( ObjectTypeResType,
|
||
FmpReturnResourceType,
|
||
&type,
|
||
(PVOID)ResourceTypeName );
|
||
if ( type == NULL ) {
|
||
return(ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND);
|
||
}
|
||
|
||
//
|
||
// Read the DebugControlFunction registry value.
|
||
//
|
||
|
||
if ( type->Flags & RESTYPE_DEBUG_CONTROL_FUNC ) {
|
||
if ( type->DebugPrefix != NULL ) {
|
||
debugPrefix = type->DebugPrefix;
|
||
} else {
|
||
debugPrefix = type->DebugPrefix;
|
||
}
|
||
monitor = FmpCreateMonitor(debugPrefix, TRUE);
|
||
if (monitor == NULL) {
|
||
return(GetLastError());
|
||
}
|
||
} else {
|
||
CL_ASSERT(FmpDefaultMonitor != NULL);
|
||
monitor = FmpDefaultMonitor;
|
||
}
|
||
|
||
//to take care of the output reference pointer which cannot be NULL.
|
||
if (!OutBuffer)
|
||
{
|
||
OutBuffer = (PUCHAR)&Dummy;
|
||
OutBufferSize = 0;
|
||
}
|
||
if (!BytesReturned)
|
||
BytesReturned = &dwTmpBytesReturned;
|
||
if (!Required)
|
||
Required = &dwTmpBytesRequired;
|
||
|
||
|
||
|
||
try {
|
||
status = RmResourceTypeControl(monitor->Binding,
|
||
ResourceTypeName,
|
||
type->DllName,
|
||
ControlCode,
|
||
InBuffer,
|
||
InBufferSize,
|
||
OutBuffer,
|
||
OutBufferSize,
|
||
BytesReturned,
|
||
Required
|
||
);
|
||
}
|
||
except( FmpRmExceptionFilter(GetExceptionCode()) ) {
|
||
status = GetExceptionCode();
|
||
|
||
ClRtlLogPrint(LOG_NOISE,
|
||
"[FM] RmResourceTypeControl issued exception %1!u!\n",
|
||
status);
|
||
}
|
||
|
||
if ( type->Flags & RESTYPE_DEBUG_CONTROL_FUNC ) {
|
||
//
|
||
// Stop this resource monitor if it is a separate resource monitor.
|
||
//
|
||
CL_ASSERT( monitor->NotifyThread != NULL );
|
||
CL_ASSERT( monitor->Process != NULL );
|
||
|
||
FmpShutdownMonitor( monitor );
|
||
|
||
}
|
||
|
||
//
|
||
// If we successfully processed this request then re-fetch any changed
|
||
// data items.
|
||
//
|
||
if ( (status == ERROR_SUCCESS ||
|
||
(status == ERROR_RESOURCE_PROPERTIES_STORED)) &&
|
||
(ControlCode & CLCTL_MODIFY_MASK) ) {
|
||
FmpHandleResourceTypeControl( type,
|
||
ControlCode,
|
||
InBuffer,
|
||
InBufferSize,
|
||
OutBuffer,
|
||
OutBufferSize,
|
||
BytesReturned,
|
||
Required );
|
||
// ignore status
|
||
}
|
||
OmDereferenceObject(type);
|
||
|
||
return(status);
|
||
|
||
} // FmpRmResourceTypeControl
|
||
|
||
|
||
|
||
|
||
/****
|
||
@func BOOL | FmpPostProcessResourceControl| For core resource, if the control
|
||
code is handled successfully by the resource dll, the fm handles
|
||
any special handling in this function.
|
||
|
||
@parm PFM_RESOURCE | Resource | Supplies the resource to be controlled.
|
||
|
||
@parm DWORD| ControlCode | Supplies the control code that defines the
|
||
structure and action of the resource control.
|
||
Values of ControlCode between 0 and 0x10000000 are reserved
|
||
for future definition and use by Microsoft. All other values
|
||
are available for use by ISVs
|
||
|
||
@parm PUCHAR | InBuffer | Supplies a pointer to the input buffer to be passed
|
||
to the resource.
|
||
|
||
@parm DWORD | InBufferSize | Supplies the size, in bytes, of the data pointed
|
||
to by lpInBuffer..
|
||
|
||
@parm PUCHAR | OutBuffer | Supplies a pointer to the output buffer to be
|
||
filled in by the resource..
|
||
|
||
@parm DWORD | OutBufferSize | Supplies the size, in bytes, of the available
|
||
space pointed to by lpOutBuffer.
|
||
|
||
@parm LPDWORD | BytesReturned | Returns the number of bytes of lpOutBuffer
|
||
actually filled in by the resource..
|
||
|
||
@parm LPDWORD | Required | The number of bytes required if OutBuffer is not big enough.
|
||
|
||
|
||
@comm Called only for core resources.
|
||
|
||
@xref
|
||
****/
|
||
|
||
DWORD
|
||
FmpPostProcessResourceControl(
|
||
IN PFM_RESOURCE Resource,
|
||
IN DWORD ControlCode,
|
||
IN PUCHAR InBuffer,
|
||
IN DWORD InBufferSize,
|
||
OUT PUCHAR OutBuffer,
|
||
IN DWORD OutBufferSize,
|
||
OUT LPDWORD BytesReturned,
|
||
OUT LPDWORD Required
|
||
)
|
||
{
|
||
DWORD dwStatus=ERROR_SUCCESS;
|
||
|
||
//handle cluster name change
|
||
switch(ControlCode)
|
||
{
|
||
case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES:
|
||
{
|
||
LPWSTR pszClusterName=NULL;
|
||
PFM_RESTYPE pResType;
|
||
|
||
//need to check this only for core resources
|
||
if (Resource->ExFlags & CLUS_FLAG_CORE)
|
||
{
|
||
pResType = Resource->Type;
|
||
//SS: chk follow the name
|
||
if (!lstrcmpiW(OmObjectId(pResType), CLUS_RESTYPE_NAME_NETNAME))
|
||
{
|
||
dwStatus = FmNetNameParseProperties(InBuffer, InBufferSize,
|
||
&pszClusterName);
|
||
if (dwStatus == ERROR_SUCCESS && pszClusterName)
|
||
{
|
||
dwStatus = FmpRegUpdateClusterName(pszClusterName);
|
||
LocalFree(pszClusterName);
|
||
} else if ( dwStatus == ERROR_FILE_NOT_FOUND ) {
|
||
dwStatus = ERROR_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
case CLUSCTL_RESOURCE_GET_CHARACTERISTICS:
|
||
{
|
||
LPDWORD pdwCharacteristics = ( LPDWORD ) OutBuffer;
|
||
//
|
||
// If the resource has dependencies, remove the quorum capable flag
|
||
//
|
||
if ( ( pdwCharacteristics != NULL ) &&
|
||
( ( *BytesReturned ) == sizeof ( DWORD ) ) &&
|
||
( ( *pdwCharacteristics ) & ( CLUS_CHAR_QUORUM ) ) )
|
||
{
|
||
FmpAcquireLocalResourceLock( Resource );
|
||
//
|
||
// The resource says it is quorum capable, however it has a dependency so it
|
||
// cant be a quorum.
|
||
//
|
||
if ( !IsListEmpty( &Resource->DependsOn ) )
|
||
{
|
||
//
|
||
// We will mask the quorum capable bit
|
||
//
|
||
*pdwCharacteristics = ( *pdwCharacteristics ) & ( ~CLUS_CHAR_QUORUM );
|
||
}
|
||
FmpReleaseLocalResourceLock( Resource );
|
||
}
|
||
break;
|
||
}
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return(dwStatus);
|
||
}
|