/*++ 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); }