/*++ Copyright (c) 1995-1997 Microsoft Corporation Module Name: resource.c Abstract: Implements the management of the resource list. This includes adding resources to the list and deleting them from the list. Author: John Vert (jvert) 1-Dec-1995 Revision History: Sivaprasad Padisetty (sivapad) 06-18-1997 Added the COM support --*/ #include "resmonp.h" #include "stdio.h" #define RESMON_MODULE RESMON_MODULE_RMAPI // // Local data // // // Function prototypes local to this module // LPWSTR GetParameter( IN HKEY ClusterKey, IN LPCWSTR ValueName ); // // Local functions // DWORD s_RmLoadResourceTypeDll( IN handle_t IDL_handle, IN LPCWSTR lpszResourceType, IN LPCWSTR lpszDllName ) { RESDLL_FNINFO ResDllFnInfo; #ifdef COMRES RESDLL_INTERFACES ResDllInterfaces; #endif DWORD dwStatus; dwStatus = RmpLoadResType( lpszResourceType, lpszDllName, &ResDllFnInfo, #ifdef COMRES &ResDllInterfaces, #endif NULL); if (ResDllFnInfo.hDll) FreeLibrary(ResDllFnInfo.hDll); #ifdef COMRES if (ResDllInterfaces.pClusterResource) IClusterResource_Release (ResDllInterfaces.pClusterResource) ; if (ResDllInterfaces.pClusterQuorumResource) IClusterQuorumResource_Release (ResDllInterfaces.pClusterQuorumResource) ; if (ResDllInterfaces.pClusterResControl) IClusterResControl_Release ( ResDllInterfaces.pClusterResControl ) ; #endif //COMRES return(dwStatus); } RESID s_RmCreateResource( IN handle_t IDL_handle, IN LPCWSTR DllName, IN LPCWSTR ResourceType, IN LPCWSTR ResourceId, IN DWORD LooksAlivePoll, IN DWORD IsAlivePoll, IN RM_NOTIFY_KEY NotifyKey, IN DWORD PendingTimeout, OUT LPDWORD Status ) /*++ Routine Description: Creates a resource to be monitored by the resource monitor. This involves allocating necessary structures, and loading its DLL. This does *NOT* insert the resource into the monitoring list or attempt to bring the resource on-line. Arguments: IDL_handle - Supplies RPC binding handle, currently unused DllName - Supplies the name of the resource DLL ResourceType - Supplies the type of resource ResourceId - Supplies the Id of this specific resource LooksAlivePoll - Supplies the LooksAlive poll interval IsAlivePoll - Supplies the IsAlive poll interval PendingTimeout - Supplies the Pending Timeout value for this resource NotifyKey - Supplies a key to be passed to the notification callback if this resource's state changes. Return Value: ResourceId - Returns a unique identifer to be used to identify this resource for later operations. --*/ { PRESOURCE Resource=NULL; DWORD Error; HKEY ResKey; PSTARTUP_ROUTINE Startup; PCLRES_FUNCTION_TABLE FunctionTable; DWORD quorumCapable; DWORD valueType; DWORD valueData; DWORD valueSize; DWORD retry; DWORD Reason; LPWSTR pszDllName = (LPWSTR) DllName; PRM_DUE_TIME_ENTRY pDueTimeEntry = NULL; CL_ASSERT(IsAlivePoll != 0); Resource = RmpAlloc(sizeof(RESOURCE)); if (Resource == NULL) { Error = ERROR_NOT_ENOUGH_MEMORY; CL_LOGFAILURE(Error); goto ErrorExit; } ZeroMemory( Resource, sizeof(RESOURCE) ); //Resource->Dll = NULL; //Resource->Flags = 0; //Resource->DllName = NULL; //Resource->ResourceType = NULL; //Resource->ResourceId = NULL; //Resource->ResourceName = NULL; //Resource->TimerEvent = NULL; //Resource->OnlineEvent = NULL; //Resource->IsArbitrated = FALSE; Resource->Signature = RESOURCE_SIGNATURE; Resource->NotifyKey = NotifyKey; Resource->LooksAlivePollInterval = LooksAlivePoll; Resource->IsAlivePollInterval = IsAlivePoll; Resource->State = ClusterResourceOffline; if ( PendingTimeout <= 10 ) { Resource->PendingTimeout = PENDING_TIMEOUT; } else { Resource->PendingTimeout = PendingTimeout; } Resource->DllName = RmpAlloc((lstrlenW(DllName) + 1)*sizeof(WCHAR)); if (Resource->DllName == NULL) { Error = ERROR_NOT_ENOUGH_MEMORY; CL_LOGFAILURE(Error); goto ErrorExit; } lstrcpyW(Resource->DllName, DllName); Resource->ResourceType = RmpAlloc((lstrlenW(ResourceType) + 1)*sizeof(WCHAR)); if (Resource->ResourceType == NULL) { Error = ERROR_NOT_ENOUGH_MEMORY; CL_LOGFAILURE(Error); goto ErrorExit; } lstrcpyW(Resource->ResourceType, ResourceType); Resource->ResourceId = RmpAlloc((lstrlenW(ResourceId) + 1)*sizeof(WCHAR)); if (Resource->ResourceId == NULL) { Error = ERROR_NOT_ENOUGH_MEMORY; CL_LOGFAILURE(Error); goto ErrorExit; } lstrcpyW(Resource->ResourceId, ResourceId); // Expand any environment variables included in the DLL path name. if ( wcschr( DllName, L'%' ) != NULL ) { pszDllName = ClRtlExpandEnvironmentStrings( DllName ); if ( pszDllName == NULL ) { Error = GetLastError(); ClRtlLogPrint(LOG_UNUSUAL, "[RM] Error expanding environment strings in '%1!ls!, error %2!u!.\n", DllName, Error); goto ErrorExit; } } // // Load the specified DLL and find the required entrypoints. // Resource->Dll = LoadLibraryW(pszDllName); if (Resource->Dll == NULL) { #ifdef COMRES HRESULT hr ; CLSID clsid ; Error = GetLastError(); // Save the previous error. Return it instead of COM error on failure ClRtlLogPrint(LOG_CRITICAL, "[RM] Error loading resource dll %1!ws!, error %2!u!.\n", pszDllName, Error); hr = CLSIDFromProgID(DllName, &clsid) ; if (FAILED (hr)) { ClRtlLogPrint(LOG_CRITICAL, "[RM] CLSIDFromProgID %1!ws!, hr = %2!u!.\n", DllName, hr); hr = CLSIDFromString( (LPWSTR) DllName, //Pointer to the string representation of the CLSID &clsid//Pointer to the CLSID ); if (FAILED (hr)) { ClRtlLogPrint(LOG_CRITICAL, "[RM] CLSIDFromString Also failed %1!ws!, hr = %2!u!.\n", DllName, hr); goto ComError ; } } if ((hr = CoCreateInstance (&clsid, NULL, CLSCTX_ALL, &IID_IClusterResource, (LPVOID *) &Resource->pClusterResource)) != S_OK) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Error CoCreateInstance Prog ID %1!ws!, error %2!u!.\n", DllName, hr); goto ComError ; } Resource->dwType = RESMON_TYPE_COM ; hr = IClusterResource_QueryInterface (Resource->pClusterResource, &IID_IClusterQuorumResource, &Resource->pClusterQuorumResource) ; if (FAILED(hr)) Resource->pClusterQuorumResource = NULL ; quorumCapable = (Resource->pClusterQuorumResource)?1:0 ; hr = IClusterResource_QueryInterface ( Resource->pClusterResource, &IID_IClusterResControl, &Resource->pClusterResControl ) ; if (FAILED(hr)) Resource->pClusterResControl = NULL ; goto comOpened ; ComError: #else Error = GetLastError(); #endif ClRtlLogPrint(LOG_CRITICAL, "[RM] Error loading resource dll %1!ws!, error %2!u!.\n", DllName, Error); CL_LOGFAILURE(Error); ClusterLogEvent2(LOG_CRITICAL, LOG_CURRENT_MODULE, __FILE__, __LINE__, RMON_CANT_LOAD_RESTYPE, sizeof(Error), &Error, DllName, ResourceType); goto ErrorExit; } #ifdef COMRES else { Resource->dwType = RESMON_TYPE_DLL ; } comOpened: #endif // // Invoke debugger if one is specified. // if ( RmpDebugger ) { // // Wait for debugger to come online. // retry = 100; while ( retry-- && !IsDebuggerPresent() ) { Sleep(100); } OutputDebugStringA("[RM] Just loaded resource DLL "); OutputDebugStringW(DllName); OutputDebugStringA("\n"); DebugBreak(); } #ifdef COMRES if (Resource->dwType == RESMON_TYPE_DLL) { #endif // // We must have a startup routine to find all the other functions. // Startup = (PSTARTUP_ROUTINE)GetProcAddress(Resource->Dll, STARTUP_ROUTINE); if ( Startup != NULL ) { FunctionTable = NULL; RmpSetMonitorState(RmonStartingResource, Resource); // // Insert the DLL & entry point info into the deadlock monitoring list. Make sure // you remove the entry after you finish the entry point call, else you will kill // this process on a FALSE deadlock positive. // pDueTimeEntry = RmpInsertDeadlockMonitorList ( Resource->DllName, Resource->ResourceType, Resource->ResourceName, L"Startup (on create)" ); try { Error = (Startup)( ResourceType, CLRES_VERSION_V1_00, CLRES_VERSION_V1_00, RmpSetResourceStatus, RmpLogEvent, &FunctionTable ); } except (EXCEPTION_EXECUTE_HANDLER) { Error = GetExceptionCode(); } RmpSetMonitorState(RmonIdle, NULL); RmpRemoveDeadlockMonitorList( pDueTimeEntry ); if ( Error != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Error on Startup call to %1!ws!, error %2!u!.\n", DllName, Error ); ClusterLogEvent2(LOG_CRITICAL, LOG_CURRENT_MODULE, __FILE__, __LINE__, RMON_CANT_INIT_RESTYPE, sizeof(Error), &Error, DllName, ResourceType); goto ErrorExit; } Error = ERROR_INVALID_DATA; if ( FunctionTable == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Error on Startup return, FunctionTable is NULL!\n"); Reason = 0; ClusterLogEvent2(LOG_CRITICAL, LOG_CURRENT_MODULE, __FILE__, __LINE__, RMON_RESTYPE_BAD_TABLE, sizeof(Reason), &Reason, DllName, ResourceType); goto ErrorExit; } if ( FunctionTable->Version != CLRES_VERSION_V1_00 ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Error on Startup return, Invalid Version Number!\n"); Reason = 1; ClusterLogEvent2(LOG_CRITICAL, LOG_CURRENT_MODULE, __FILE__, __LINE__, RMON_RESTYPE_BAD_TABLE, sizeof(Reason), &Reason, DllName, ResourceType); goto ErrorExit; } if ( FunctionTable->TableSize != CLRES_V1_FUNCTION_SIZE ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Error on Startup return, Invalid function table size!\n"); Reason = 2; ClusterLogEvent2(LOG_CRITICAL, LOG_CURRENT_MODULE, __FILE__, __LINE__, RMON_RESTYPE_BAD_TABLE, sizeof(Reason), &Reason, DllName, ResourceType); goto ErrorExit; } #ifdef COMRES Resource->pOpen = FunctionTable->V1Functions.Open; Resource->pClose = FunctionTable->V1Functions.Close; Resource->pOnline = FunctionTable->V1Functions.Online; Resource->pOffline = FunctionTable->V1Functions.Offline; Resource->pTerminate = FunctionTable->V1Functions.Terminate; Resource->pLooksAlive = FunctionTable->V1Functions.LooksAlive; Resource->pIsAlive = FunctionTable->V1Functions.IsAlive; Resource->pArbitrate = FunctionTable->V1Functions.Arbitrate; Resource->pRelease = FunctionTable->V1Functions.Release; Resource->pResourceControl = FunctionTable->V1Functions.ResourceControl; Resource->pResourceTypeControl = FunctionTable->V1Functions.ResourceTypeControl; Error = ERROR_INVALID_DATA; if ( Resource->pOpen == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null Open routine for resource dll %1!ws!\n", DllName); goto ErrorExit; } if ( Resource->pClose == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null Close routine for resource dll %1!ws!\n", DllName); goto ErrorExit; } if ( Resource->pOnline == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null Online routine for resource dll %1!ws!\n", DllName); goto ErrorExit; } if ( Resource->pOffline == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null Offline routine for resource dll %1!ws!\n", DllName); goto ErrorExit; } if ( Resource->pTerminate == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null Terminate routine for resource dll %1!ws!\n", DllName); goto ErrorExit; } if ( Resource->pLooksAlive == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null LooksAlive routine for resource dll %1!ws!\n", DllName); goto ErrorExit; } if ( Resource->pIsAlive == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null IsAlive routine for resource dll %1!ws!\n", DllName); goto ErrorExit; } } else { ClRtlLogPrint(LOG_CRITICAL, "[RM] Could not find startup routine in resource DLL %1!ws!.\n", DllName); Error = ERROR_INVALID_DATA; goto ErrorExit; } if ( (Resource->pArbitrate) && (Resource->pRelease) ) { quorumCapable = 1; } else { quorumCapable = 0; } } #else // COMRES Resource->Open = FunctionTable->V1Functions.Open; Resource->Close = FunctionTable->V1Functions.Close; Resource->Online = FunctionTable->V1Functions.Online; Resource->Offline = FunctionTable->V1Functions.Offline; Resource->Terminate = FunctionTable->V1Functions.Terminate; Resource->LooksAlive = FunctionTable->V1Functions.LooksAlive; Resource->IsAlive = FunctionTable->V1Functions.IsAlive; Resource->Arbitrate = FunctionTable->V1Functions.Arbitrate; Resource->Release = FunctionTable->V1Functions.Release; Resource->ResourceControl = FunctionTable->V1Functions.ResourceControl; Resource->ResourceTypeControl = FunctionTable->V1Functions.ResourceTypeControl; Error = ERROR_INVALID_DATA; if ( Resource->Open == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null Open routine for resource dll %1!ws!\n", pszDllName); goto ErrorExit; } if ( Resource->Close == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null Close routine for resource dll %1!ws!\n", pszDllName); goto ErrorExit; } if ( Resource->Online == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null Online routine for resource dll %1!ws!\n", pszDllName); goto ErrorExit; } if ( Resource->Offline == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null Offline routine for resource dll %1!ws!\n", pszDllName); goto ErrorExit; } if ( Resource->Terminate == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null Terminate routine for resource dll %1!ws!\n", pszDllName); goto ErrorExit; } if ( Resource->LooksAlive == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null LooksAlive routine for resource dll %1!ws!\n", pszDllName); goto ErrorExit; } if ( Resource->IsAlive == NULL ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Startup returned null IsAlive routine for resource dll %1!ws!\n", pszDllName); goto ErrorExit; } } else { ClRtlLogPrint(LOG_CRITICAL, "[RM] Could not find startup routine in resource DLL %1!ws!.\n", pszDllName); Error = ERROR_INVALID_DATA; goto ErrorExit; } if ( (Resource->Arbitrate) && (Resource->Release) ) { quorumCapable = 1; } else { quorumCapable = 0; } #endif // COMRES Resource->State = ClusterResourceOffline; // // Open the resource's cluster registry key so that it can // be easily accessed from the Create entrypoint. // Error = ClusterRegOpenKey(RmpResourcesKey, ResourceId, KEY_READ, &ResKey); if (Error != ERROR_SUCCESS) { CL_LOGFAILURE(Error); goto ErrorExit; } // // Get the resource name. // Resource->ResourceName = GetParameter( ResKey, CLUSREG_NAME_RES_NAME ); if ( Resource->ResourceName == NULL ) { Error = GetLastError(); ClusterRegCloseKey(ResKey); ClRtlLogPrint(LOG_UNUSUAL, "[RM] Error reading resource name for %1!ws!, error %2!u!.\n", Resource->ResourceId, Error); CL_LOGFAILURE(Error); goto ErrorExit; } // // Call Open entrypoint. // This is done with the lock held to serialize calls to the // resource DLL and serialize access to the shared memory region. // AcquireListLock(); RmpSetMonitorState(RmonInitializingResource, Resource); // // Insert the DLL & entry point info into the deadlock monitoring list. Make sure // you remove the entry after you finish the entry point call, else you will kill // this process on a FALSE deadlock positive. // pDueTimeEntry = RmpInsertDeadlockMonitorList ( Resource->DllName, Resource->ResourceType, Resource->ResourceName, L"Open" ); // // N.B. This is the only call that we make without locking the // eventlist lock! We can't, because we don't know that the event // list is yet. // try { #ifdef COMRES Resource->Id = RESMON_OPEN (Resource, ResKey) ; #else Resource->Id = (Resource->Open)(Resource->ResourceName, ResKey, Resource ); #endif } except (EXCEPTION_EXECUTE_HANDLER) { SetLastError(GetExceptionCode()); Resource->Id = 0; } RmpRemoveDeadlockMonitorList( pDueTimeEntry ); if (Resource->Id == 0) { Error = GetLastError(); } else { Error = RmpInsertResourceList(Resource, NULL); } //set the monitor state and close the key RmpSetMonitorState(RmonIdle, NULL); ClusterRegCloseKey(ResKey); if (Error != ERROR_SUCCESS) { //CL_LOGFAILURE(Error); ClRtlLogPrint(LOG_UNUSUAL, "[RM] RmpInsertResourceList failed, returned %1!u!\n", Error); ReleaseListLock(); goto ErrorExit; } ReleaseListLock(); if (Resource->Id == 0) { ClRtlLogPrint(LOG_CRITICAL, "[RM] Open of resource %1!ws! returned null!\n", Resource->ResourceName); if ( Error == ERROR_SUCCESS ) { Error = ERROR_RESOURCE_NOT_FOUND; } //CL_LOGFAILURE(Error); goto ErrorExit; } *Status = ERROR_SUCCESS; if ( pszDllName != DllName ) { LocalFree( pszDllName ); } // // Resource object has been successfully loaded into memory and // its entrypoints determined. We now have a valid RESID that // can be used in subsequent calls. // return((RESID)Resource); ErrorExit: if (Resource != NULL) { if (Resource->Dll != NULL) { FreeLibrary(Resource->Dll); } #ifdef COMRES if (Resource->pClusterResource) IClusterResource_Release (Resource->pClusterResource) ; if (Resource->pClusterQuorumResource) IClusterQuorumResource_Release (Resource->pClusterQuorumResource) ; if (Resource->pClusterResControl) IClusterResControl_Release ( Resource->pClusterResControl ) ; #endif RmpFree(Resource->DllName); RmpFree(Resource->ResourceType); RmpFree(Resource->ResourceName); RmpFree(Resource->ResourceId); RmpFree(Resource); } if ( pszDllName != DllName ) { LocalFree( pszDllName ); } ClRtlLogPrint(LOG_CRITICAL, "[RM] Failed creating resource %1!ws!, error %2!u!.\n", ResourceId, Error); *Status = Error; return(0); } // RmCreateResource VOID s_RmCloseResource( IN OUT RESID *ResourceId ) /*++ Routine Description: Closes the specified resource. This includes removing it from the poll list, freeing any associated memory, and unloading its DLL. Arguments: ResourceId - Supplies a pointer to the resource ID. This will be set to NULL after cleanup is complete to indicate to RPC that the client side context can be destroyed. Return Value: None. --*/ { PRESOURCE Resource; BOOL Closed; PRM_DUE_TIME_ENTRY pDueTimeEntry = NULL; Resource = (PRESOURCE)*ResourceId; CL_ASSERT(Resource->Signature == RESOURCE_SIGNATURE); CL_ASSERT(Resource->Flags & RESOURCE_INSERTED); AcquireListLock(); if (Resource->ListEntry.Flink != NULL) { RmpRemoveResourceList(Resource); Closed = FALSE; } else { Closed = TRUE; } ReleaseListLock(); if (!Closed) { // // Call the DLL to close the resource. // AcquireEventListLock( (PPOLL_EVENT_LIST)Resource->EventList ); // // If the Online Thread is still pending, wait a little bit for it. // if ( Resource->TimerEvent ) { SetEvent( Resource->TimerEvent ); Resource->TimerEvent = NULL; } Resource->dwEntryPoint = RESDLL_ENTRY_CLOSE; RmpSetMonitorState(RmonDeletingResource, Resource); // // Insert the DLL & entry point info into the deadlock monitoring list. Make sure // you remove the entry after you finish the entry point call, else you will kill // this process on a FALSE deadlock positive. // pDueTimeEntry = RmpInsertDeadlockMonitorList ( Resource->DllName, Resource->ResourceType, Resource->ResourceName, L"Close" ); try { #ifdef COMRES RESMON_CLOSE (Resource) ; #else (Resource->Close)(Resource->Id); #endif } except (EXCEPTION_EXECUTE_HANDLER) { } Resource->dwEntryPoint = 0; RmpSetMonitorState(RmonIdle, Resource); RmpRemoveDeadlockMonitorList ( pDueTimeEntry ); ReleaseEventListLock( (PPOLL_EVENT_LIST)Resource->EventList ); } Resource->Signature = 0; if ( Resource->OnlineEvent ) { SetEvent( Resource->OnlineEvent ); CloseHandle( Resource->OnlineEvent ); Resource->OnlineEvent = NULL; } // // Free the resource dll. // #ifdef COMRES if (Resource->dwType == RESMON_TYPE_DLL) { FreeLibrary(Resource->Dll); } else { IClusterResource_Release (Resource->pClusterResource) ; if (Resource->pClusterQuorumResource) IClusterQuorumResource_Release (Resource->pClusterQuorumResource) ; if (Resource->pClusterResControl) IClusterResControl_Release ( Resource->pClusterResControl ) ; } #else FreeLibrary(Resource->Dll); #endif RmpFree(Resource->DllName); RmpFree(Resource->ResourceType); RmpFree(Resource->ResourceName); RmpFree(Resource->ResourceId); RmpFree(Resource); *ResourceId = NULL; } // RmCloseResource VOID RPC_RESID_rundown( IN RESID Resource ) /*++ Routine Description: RPC rundown procedure for a RESID. Just closes the handle. Arguments: Resource - supplies the RESID that is to be rundown. Return Value: None. --*/ { // // Chittur Subbaraman (chitturs) - 5/10/2001 // // Don't do anything on RPC rundown. If clussvc dies, then resmon main thread detects it and // runs down (close, terminate) resources. Merely, closing the resource here may cause // it to be delivered when resource dlls don't expect it. // #if 0 s_RmCloseResource(&Resource); #endif } // RESID_rundown error_status_t s_RmChangeResourceParams( IN RESID ResourceId, IN DWORD LooksAlivePoll, IN DWORD IsAlivePoll, IN DWORD PendingTimeout ) /*++ Routine Description: Changes the poll intervals defined for a resource. Arguments: ResourceId - Supplies the resource ID. LooksAlivePoll - Supplies the new LooksAlive poll in ms units IsAlivePoll - Supplies the new IsAlive poll in ms units Return Value: ERROR_SUCCESS if successful Win32 error otherwise --*/ { PRESOURCE Resource; BOOL Inserted; // // If the resmon is shutting down, just return since you can't trust any resource structures // accessed below. // if ( RmpShutdown ) return ( ERROR_SUCCESS ); Resource = (PRESOURCE)ResourceId; CL_ASSERT(Resource->Signature == RESOURCE_SIGNATURE); AcquireListLock(); Inserted = (Resource->Flags & RESOURCE_INSERTED); if (Inserted) { // // Remove the resource from the list, update its properties, // and reinsert it. The reinsertion will put it back in the // right spot to reflect the new poll intervals. // RmpRemoveResourceList(Resource); } Resource->LooksAlivePollInterval = LooksAlivePoll; Resource->IsAlivePollInterval = IsAlivePoll; Resource->PendingTimeout = PendingTimeout; if (Inserted) { RmpInsertResourceList( Resource, (PPOLL_EVENT_LIST) Resource->EventList); } ReleaseListLock(); return(ERROR_SUCCESS); } // RmChangeResourcePoll error_status_t s_RmArbitrateResource( IN RESID ResourceId ) /*++ Routine Description: Arbitrate for the resource. Arguments: ResourceId - Supplies the resource to be arbitrated. Return Value: ERROR_SUCCESS if successful. A Win32 error code on failure. --*/ { PRESOURCE Resource; DWORD status; PRM_DUE_TIME_ENTRY pDueTimeEntry = NULL; Resource = (PRESOURCE)ResourceId; CL_ASSERT(Resource->Signature == RESOURCE_SIGNATURE); #ifdef COMRES if (Resource->dwType == RESMON_TYPE_DLL) { if ( (Resource->pArbitrate == NULL) || (Resource->pRelease == NULL) ) { return(ERROR_NOT_QUORUM_CAPABLE); } } else { if (!Resource->pClusterQuorumResource) return(ERROR_NOT_QUORUM_CAPABLE); } #else if ( (Resource->Arbitrate == NULL) || (Resource->Release == NULL) ) { return(ERROR_NOT_QUORUM_CAPABLE); } #endif // // Chittur Subbaraman (chitturs) - 10/15/99 // // Commenting out lock acquisition - This is done so that the // arbitration request can proceed into the disk resource without // any blockage. There have been cases where either some resource // gets blocked in its "IsAlive" with this lock held or the resmon // itself calling into clussvc to set a property (for instance) and // this call gets blocked there. This results in an arbitration stall // and the clussvc on this node dies. Note that arbitrate only needs to // be serialized with release and the disk resource is supposed // to take care of that. // #if 0 // // Lock the resource list and attempt to bring the resource on-line. // The lock is required to synchronize access to the resource list and // to serialize any calls to resource DLLs. Only one thread may be // calling a resource DLL at any time. This prevents resource DLLs // from having to worry about being thread-safe. // AcquireEventListLock( (PPOLL_EVENT_LIST)Resource->EventList ); #else // // Try to acquire spin lock for synchronizing with resource rundown. // Return failure if you cannot get the lock. // if ( !RmpAcquireSpinLock( Resource, FALSE ) ) return ( ERROR_BUSY ); #endif // // Update shared state to indicate we are arbitrating a resource // RmpSetMonitorState(RmonArbitrateResource, Resource); // // Insert the DLL & entry point info into the deadlock monitoring list. Make sure // you remove the entry after you finish the entry point call, else you will kill // this process on a FALSE deadlock positive. // pDueTimeEntry = RmpInsertDeadlockMonitorList ( Resource->DllName, Resource->ResourceType, Resource->ResourceName, L"Arbitrate" ); #ifdef COMRES status = RESMON_ARBITRATE (Resource, RmpLostQuorumResource) ; #else status = (Resource->Arbitrate)(Resource->Id, RmpLostQuorumResource); #endif if (status == ERROR_SUCCESS) { Resource->IsArbitrated = TRUE; } RmpRemoveDeadlockMonitorList( pDueTimeEntry ); RmpSetMonitorState(RmonIdle, NULL); #if 0 ReleaseEventListLock( (PPOLL_EVENT_LIST)Resource->EventList ); #else RmpReleaseSpinLock( Resource ); #endif return(status); } // s_RmArbitrateResource( error_status_t s_RmReleaseResource( IN RESID ResourceId ) /*++ Routine Description: Release the resource. Arguments: ResourceId - Supplies the resource to be released. Return Value: ERROR_SUCCESS if successful. A Win32 error code on failure. --*/ { PRESOURCE Resource; DWORD status; PRM_DUE_TIME_ENTRY pDueTimeEntry = NULL; Resource = (PRESOURCE)ResourceId; CL_ASSERT(Resource->Signature == RESOURCE_SIGNATURE); #ifdef COMRES if (Resource->dwType == RESMON_TYPE_DLL) { if (Resource->pRelease == NULL) { return(ERROR_NOT_QUORUM_CAPABLE); } } else { if (!Resource->pClusterQuorumResource) return(ERROR_NOT_QUORUM_CAPABLE); } #else if ( Resource->Release == NULL ) { return(ERROR_NOT_QUORUM_CAPABLE); } #endif // // Lock the resource list and attempt to bring the resource on-line. // The lock is required to synchronize access to the resource list and // to serialize any calls to resource DLLs. Only one thread may be // calling a resource DLL at any time. This prevents resource DLLs // from having to worry about being thread-safe. // #if 0 // Not needed right now AcquireEventListLock( (PPOLL_EVENT_LIST)Resource->EventList ); #endif // // Update shared state to indicate we ar arbitrating a resource // RmpSetMonitorState(RmonReleaseResource, Resource); // // Insert the DLL & entry point info into the deadlock monitoring list. Make sure // you remove the entry after you finish the entry point call, else you will kill // this process on a FALSE deadlock positive. // pDueTimeEntry = RmpInsertDeadlockMonitorList ( Resource->DllName, Resource->ResourceType, Resource->ResourceName, L"Release" ); #ifdef COMRES status = RESMON_RELEASE (Resource) ; #else status = (Resource->Release)(Resource->Id); #endif Resource->IsArbitrated = FALSE; RmpSetMonitorState(RmonIdle, NULL); RmpRemoveDeadlockMonitorList( pDueTimeEntry ); #if 0 // Not needed right now ReleaseEventListLock( (PPOLL_EVENT_LIST)Resource->EventList ); #endif return(status); } // s_RmReleaseResource( error_status_t s_RmOnlineResource( IN RESID ResourceId, OUT LPDWORD pdwState ) /*++ Routine Description: Brings the specified resource into the online state. Arguments: ResourceId - Supplies the resource to be brought online. pdwState - The new state of the resource is returned. Return Value: --*/ { PRESOURCE Resource; DWORD status; HANDLE timerThread; HANDLE eventHandle = NULL; DWORD threadId; BOOL fAddPollEvent = FALSE; PRM_DUE_TIME_ENTRY pDueTimeEntry = NULL; Resource = (PRESOURCE)ResourceId; CL_ASSERT(Resource->Signature == RESOURCE_SIGNATURE); CL_ASSERT(Resource->EventHandle == NULL ); *pdwState = ClusterResourceFailed; if ( Resource->State > ClusterResourcePending ) { return(ERROR_INVALID_STATE); } // // Create an event to allow the SetResourceStatus callback to synchronize // execution with this thread. // if ( Resource->OnlineEvent ) { return(ERROR_NOT_READY); } Resource->OnlineEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( Resource->OnlineEvent == NULL ) { return(GetLastError()); } // // Lock the resource list and attempt to bring the resource on-line. // The lock is required to synchronize access to the resource list and // to serialize any calls to resource DLLs. Only one thread may be // calling a resource DLL at any time. This prevents resource DLLs // from having to worry about being thread-safe. // AcquireEventListLock( (PPOLL_EVENT_LIST)Resource->EventList ); // // Update shared state to indicate we are bringing a resource online // RmpSetMonitorState(RmonOnlineResource, Resource); // // Insert the DLL & entry point info into the deadlock monitoring list. Make sure // you remove the entry after you finish the entry point call, else you will kill // this process on a FALSE deadlock positive. // pDueTimeEntry = RmpInsertDeadlockMonitorList ( Resource->DllName, Resource->ResourceType, Resource->ResourceName, L"Online" ); // // Call Online entrypoint. Regardless of whether this succeeds or // not, the resource has been successfully added to the list. If the // online call fails, the resource immediately enters the failed state. // Resource->CheckPoint = 0; try { #ifdef COMRES status = RESMON_ONLINE (Resource, &eventHandle) ; #else status = (Resource->Online)(Resource->Id, &eventHandle); #endif } except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } RmpRemoveDeadlockMonitorList( pDueTimeEntry ); if (status == ERROR_SUCCESS) { SetEvent( Resource->OnlineEvent ); CloseHandle( Resource->OnlineEvent ); Resource->OnlineEvent = NULL; Resource->State = ClusterResourceOnline; if ( eventHandle ) { fAddPollEvent = TRUE; } } else if ( status == ERROR_IO_PENDING ) { status = ERROR_SUCCESS; // // If the Resource DLL returns pending, then start a timer. // CL_ASSERT(Resource->TimerEvent == NULL ); Resource->TimerEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( Resource->TimerEvent == NULL ) { CL_UNEXPECTED_ERROR(status = GetLastError()); } else { timerThread = CreateThread( NULL, 0, RmpTimerThread, Resource, 0, &threadId ); if ( timerThread == NULL ) { CL_UNEXPECTED_ERROR(status = GetLastError()); } else { // // Chittur Subbaraman (chitturs) - 1/12/99 // // Raise the timer thread priority to highest. This // is necessary to avoid certain cases in which the // timer thread is sluggish to close out the timer event // handle before a second online. Note that there are // no major performance implications by doing this since // the timer thread is in a wait state most of the time. // if ( !SetThreadPriority( timerThread, THREAD_PRIORITY_HIGHEST ) ) { ClRtlLogPrint(LOG_UNUSUAL, "[RM] s_RmOnlineResource: Error setting priority of timer " "thread for resource %1!ws!\n", Resource->ResourceName); CL_LOGFAILURE( GetLastError() ); } CloseHandle( timerThread ); Resource->State = ClusterResourceOnlinePending; // // If we have an event handle, then add it to our list. // if ( eventHandle ) { fAddPollEvent = TRUE; } } } SetEvent( Resource->OnlineEvent ); } else { CloseHandle( Resource->OnlineEvent ); Resource->OnlineEvent = NULL; ClRtlLogPrint(LOG_CRITICAL, "[RM] OnlineResource failed, resource %1!ws!, status = %2!u!.\n", Resource->ResourceName, status); ClusterLogEvent1(LOG_CRITICAL, LOG_CURRENT_MODULE, __FILE__, __LINE__, RMON_ONLINE_FAILED, sizeof(status), &status, Resource->ResourceName); Resource->State = ClusterResourceFailed; } *pdwState = Resource->State; RmpSetMonitorState(RmonIdle, NULL); ReleaseEventListLock( (PPOLL_EVENT_LIST)Resource->EventList ); // // Notify poller thread that the list has changed // if ( fAddPollEvent ) { ClRtlLogPrint(LOG_NOISE, "[RM] s_RmOnlineResource: Adding event 0x%1!08lx! for resource %2!ws!\n", eventHandle, Resource->ResourceName); status = RmpAddPollEvent( (PPOLL_EVENT_LIST)Resource->EventList, eventHandle, Resource ); if ( status != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] s_RmOnlineResource: Failed to add event 0x%1!08lx! for resource %2!ws!, error %3!u!.\n", eventHandle, Resource->ResourceName, status ); CL_LOGFAILURE(status); Resource->State = ClusterResourceFailed; } } RmpSignalPoller((PPOLL_EVENT_LIST)Resource->EventList); return(status); } // RmOnlineResource VOID s_RmTerminateResource( IN RESID ResourceId ) /*++ Routine Description: Brings the specified resource into the offline state immediately. Arguments: ResourceId - Supplies the resource to be brought online. Return Value: The new state of the resource. --*/ { DWORD State; RmpOfflineResource(ResourceId, FALSE, &State); return; } // RmTerminateResource error_status_t s_RmOfflineResource( IN RESID ResourceId, OUT LPDWORD pdwState ) /*++ Routine Description: Brings the specified resource into the offline state by shutting it down gracefully. Arguments: ResourceId - Supplies the resource to be brought online. pdwState - Returns the new state of the resource. Return Value: ERROR_SUCCESS if successful, else returns code. --*/ { return(RmpOfflineResource(ResourceId, TRUE, pdwState)); } // RmOfflineResource error_status_t s_RmFailResource( IN RESID ResourceId ) /*++ Routine Description: Fail the given resource. Arguments: ResourceId - Supplies the resource ID. Return Value: ERROR_SUCCESS if successful Win32 error otherwise --*/ { PRESOURCE Resource; Resource = (PRESOURCE)ResourceId; CL_ASSERT(Resource->Signature == RESOURCE_SIGNATURE); if ( Resource->State == ClusterResourceOnline ) { Resource->State = ClusterResourceFailed; RmpPostNotify(Resource, NotifyResourceStateChange); return(ERROR_SUCCESS); } else { return(ERROR_RESMON_INVALID_STATE); } } // RmChangeResourcePoll error_status_t s_RmShutdownProcess( IN handle_t IDL_handle ) /*++ Routine Description: Set the shutdown flag and trigger a poller thread to exit. The termination of any poller thread will notify the main thread to clean up and shutdown. Arguments: IDL_handle - Supplies RPC binding handle, currently unused Return Value: ERROR_SUCCESS --*/ { // // Check if we've already been called here before. This can happen // as a result of the Failover Manager calling us to perform a clean // shutdown. The main thread also will call here, in case it is shutting // down because of a failure of one of the threads. // if ( !RmpShutdown ) { RmpShutdown = TRUE; // // Wake up the main thread so that it can cleanup. // SetEvent( RmpRewaitEvent ); } return(ERROR_SUCCESS); } // RmShutdownProcess error_status_t s_RmResourceControl( IN RESID ResourceId, IN DWORD ControlCode, IN PUCHAR InBuffer, IN DWORD InBufferSize, OUT PUCHAR OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned, OUT LPDWORD Required ) /*++ Routine Description: Process a resource control request. Arguments: ResourceId - the resource that is being controlled. ControlCode - the control request, reduced to just the function code. InBuffer - the input buffer for this control request. InBufferSize - the size of the input buffer. OutBuffer - the output buffer. OutBufferSize - the size of the output buffer. BytesReturned - the number of bytes returned. Required - the number of bytes required if OutBuffer is not big enough. Return Value: ERROR_SUCCESS if successful A Win32 error code on failure --*/ { PRESOURCE Resource; DWORD status = ERROR_INVALID_FUNCTION; PRM_DUE_TIME_ENTRY pDueTimeEntry = NULL; Resource = (PRESOURCE)ResourceId; CL_ASSERT(Resource->Signature == RESOURCE_SIGNATURE); // Inbuffer is [unique]. if ( InBuffer == NULL ) InBufferSize = 0; else if ( InBufferSize == 0 ) InBuffer = NULL; // // Lock the resource list and send down the control request. // The lock is required to synchronize access to the resource list and // to serialize any calls to resource DLLs. Only one thread may be // calling a resource DLL at any time. This prevents resource DLLs // from having to worry about being thread-safe. // AcquireEventListLock( (PPOLL_EVENT_LIST)Resource->EventList ); // // If the control code is a notification for resource name change, modify the resource // name field in the resource structure to reflect that. // if ( ControlCode == CLUSCTL_RESOURCE_SET_NAME ) { LPWSTR lpszNewBuffer, lpszOldBuffer; lpszNewBuffer = LocalAlloc ( LPTR, InBufferSize ); if ( lpszNewBuffer != NULL ) { lstrcpyW ( lpszNewBuffer, ( LPWSTR ) InBuffer ); lpszOldBuffer = InterlockedExchangePointer ( &Resource->ResourceName, lpszNewBuffer ); LocalFree ( lpszOldBuffer ); } } // // Insert the DLL & entry point info into the deadlock monitoring list. Make sure // you remove the entry after you finish the entry point call, else you will kill // this process on a FALSE deadlock positive. // pDueTimeEntry = RmpInsertDeadlockMonitorList ( Resource->DllName, Resource->ResourceType, Resource->ResourceName, L"Resource control" ); #ifdef COMRES if (Resource->pResourceControl || Resource->pClusterResControl) { RmpSetMonitorState(RmonResourceControl, Resource); status = RESMON_RESOURCECONTROL( Resource, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned ); RmpSetMonitorState(RmonIdle, NULL); } #else if ( Resource->ResourceControl ) { RmpSetMonitorState(RmonResourceControl, Resource); status = (Resource->ResourceControl)( Resource->Id, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned ); RmpSetMonitorState(RmonIdle, NULL); } #endif RmpRemoveDeadlockMonitorList( pDueTimeEntry ); if ( status == ERROR_INVALID_FUNCTION ) { DWORD characteristics = CLUS_CHAR_UNKNOWN; switch ( ControlCode ) { case CLUSCTL_RESOURCE_GET_COMMON_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( RmpResourceCommonProperties, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_GET_CLASS_INFO: if ( OutBufferSize < sizeof(CLUS_RESOURCE_CLASS_INFO) ) { *BytesReturned = 0; *Required = sizeof(CLUS_RESOURCE_CLASS_INFO); if ( OutBuffer == NULL ) { status = ERROR_SUCCESS; } else { status = ERROR_MORE_DATA; } } else { PCLUS_RESOURCE_CLASS_INFO ptrResClassInfo = (PCLUS_RESOURCE_CLASS_INFO) OutBuffer; ptrResClassInfo->rc = CLUS_RESCLASS_UNKNOWN; ptrResClassInfo->SubClass = 0; *BytesReturned = sizeof(CLUS_RESOURCE_CLASS_INFO); status = ERROR_SUCCESS; } break; case CLUSCTL_RESOURCE_GET_CHARACTERISTICS: #ifdef COMRES if (Resource->dwType == RESMON_TYPE_DLL) { if ( (Resource->pArbitrate != NULL) && (Resource->pRelease != NULL) ) { characteristics = CLUS_CHAR_QUORUM; } } else { if (!Resource->pClusterQuorumResource) characteristics = CLUS_CHAR_QUORUM; } #else if ( (Resource->Arbitrate != NULL) && (Resource->Release != NULL) ) { characteristics = CLUS_CHAR_QUORUM; } #endif if ( OutBufferSize < sizeof(DWORD) ) { *BytesReturned = 0; *Required = sizeof(DWORD); if ( OutBuffer == NULL ) { status = ERROR_SUCCESS; } else { status = ERROR_MORE_DATA; } } else { *BytesReturned = sizeof(DWORD); *(LPDWORD)OutBuffer = characteristics; status = ERROR_SUCCESS; } break; case CLUSCTL_RESOURCE_GET_FLAGS: status = RmpResourceGetFlags( Resource, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_GET_REQUIRED_DEPENDENCIES: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_ENUM_COMMON_PROPERTIES: status = RmpResourceEnumCommonProperties( OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_GET_RO_COMMON_PROPERTIES: status = RmpResourceGetCommonProperties( Resource, TRUE, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_GET_COMMON_PROPERTIES: status = RmpResourceGetCommonProperties( Resource, FALSE, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_VALIDATE_COMMON_PROPERTIES: status = RmpResourceValidateCommonProperties( Resource, InBuffer, InBufferSize ); break; case CLUSCTL_RESOURCE_SET_COMMON_PROPERTIES: status = RmpResourceSetCommonProperties( Resource, InBuffer, InBufferSize ); break; case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES: status = RmpResourceEnumPrivateProperties( Resource, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_GET_RO_PRIVATE_PROPERTIES: if ( OutBufferSize < sizeof(DWORD) ) { *BytesReturned = 0; *Required = sizeof(DWORD); if ( OutBuffer == NULL ) { status = ERROR_SUCCESS; } else { status = ERROR_MORE_DATA; } } else { LPDWORD ptrDword = (LPDWORD) OutBuffer; *ptrDword = 0; *BytesReturned = sizeof(DWORD); status = ERROR_SUCCESS; } break; case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES: status = RmpResourceGetPrivateProperties( Resource, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES: status = RmpResourceValidatePrivateProperties( Resource, InBuffer, InBufferSize ); break; case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES: status = RmpResourceSetPrivateProperties( Resource, InBuffer, InBufferSize ); break; case CLUSCTL_RESOURCE_SET_NAME: // // Chittur Subbaraman (chitturs) - 6/28/99 // // The setting of the name in the cluster registry is done // in NT5 by clussvc. So, resmon does not have to do any work // except return a success code in case a resource DLL returns // ERROR_INVALID_FUNCTION. // status = ERROR_SUCCESS; break; default: break; } } else { // If the function is returning a buffer size without // copying data, move this info around to satisfy RPC. if ( *BytesReturned > OutBufferSize ) { *Required = *BytesReturned; *BytesReturned = 0; status = ERROR_MORE_DATA; } } if ( ( status != ERROR_SUCCESS ) && ( status != ERROR_MORE_DATA ) && ( status != ERROR_INVALID_FUNCTION ) ) { ClRtlLogPrint(LOG_UNUSUAL, "[RM] s_RmResourceControl: Resource <%1!ws!> control operation " "0x%2!08lx! gives status=%3!u!...\n", Resource->ResourceName, ControlCode, status); } ReleaseEventListLock( (PPOLL_EVENT_LIST)Resource->EventList ); return(status); } // RmResourceControl error_status_t s_RmResourceTypeControl( IN handle_t IDL_handle, IN LPCWSTR ResourceTypeName, IN LPCWSTR DllName, IN DWORD ControlCode, IN PUCHAR InBuffer, IN DWORD InBufferSize, OUT PUCHAR OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned, OUT LPDWORD Required ) /*++ Routine Description: Process a resource type control request. Arguments: IDL_handle - not used. ResourceTypeName - the resource type name that is being controlled. DllName - the name of the dll. ControlCode - the control request, reduced to just the function code. InBuffer - the input buffer for this control request. InBufferSize - the size of the input buffer. OutBuffer - the output buffer. OutBufferSize - the size of the output buffer. BytesReturned - the number of bytes returned. Required - the number of bytes required if OutBuffer is not big enough. Return Value: ERROR_SUCCESS if successful A Win32 error code on failure --*/ { RESDLL_FNINFO ResDllFnInfo; #ifdef COMRES RESDLL_INTERFACES ResDllInterfaces; #endif DWORD status = ERROR_INVALID_FUNCTION; DWORD characteristics = CLUS_CHAR_UNKNOWN; PRM_DUE_TIME_ENTRY pDueTimeEntry = NULL; // InBuffer is [unique]. if ( InBuffer == NULL ) InBufferSize = 0; else if ( InBufferSize == 0 ) InBuffer = NULL; status = RmpLoadResType(ResourceTypeName, DllName, &ResDllFnInfo, #ifdef COMRES &ResDllInterfaces, #endif &characteristics); if (status != ERROR_SUCCESS) { return(status); } status = ERROR_INVALID_FUNCTION; if (ResDllFnInfo.hDll && ResDllFnInfo.pResFnTable) { PRESOURCE_TYPE_CONTROL_ROUTINE resourceTypeControl = NULL ; resourceTypeControl = ResDllFnInfo.pResFnTable->V1Functions.ResourceTypeControl; if (resourceTypeControl) { RmpSetMonitorState(RmonResourceTypeControl, NULL); // // Insert the DLL & entry point info into the deadlock monitoring list. Make sure // you remove the entry after you finish the entry point call, else you will kill // this process on a FALSE deadlock positive. // pDueTimeEntry = RmpInsertDeadlockMonitorList ( DllName, ResourceTypeName, NULL, L"Resource type control" ); status = (resourceTypeControl)( ResourceTypeName, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned ); RmpSetMonitorState(RmonIdle, NULL); RmpRemoveDeadlockMonitorList( pDueTimeEntry ); } } #ifdef COMRES else if (ResDllInterfaces.pClusterResControl) { HRESULT hr ; VARIANT vtIn, vtOut ; SAFEARRAY sfIn = {1, 0, 1, 0, InBuffer, {InBufferSize, 0} } ; SAFEARRAY sfOut = {1, FADF_FIXEDSIZE, 1, 0, OutBuffer, {OutBufferSize, 0} } ; SAFEARRAY *psfOut = &sfOut ; BSTR pbResourceTypeName ; vtIn.vt = VT_ARRAY | VT_UI1 ; vtOut.vt = VT_ARRAY | VT_UI1 | VT_BYREF ; vtIn.parray = &sfIn ; vtOut.pparray = &psfOut ; pbResourceTypeName = SysAllocString (ResourceTypeName) ; if (pbResourceTypeName == NULL) { CL_LOGFAILURE( ERROR_NOT_ENOUGH_MEMORY) ; goto FnExit ; // Use the default processing } RmpSetMonitorState(RmonResourceTypeControl, NULL); pDueTimeEntry = RmpInsertDeadlockMonitorList ( DllName, ResourceTypeName, NULL, L"Resource Type Control" ); hr = IClusterResControl_ResourceTypeControl ( ResDllInterfaces.pClusterResControl, pbResourceTypeName, ControlCode, &vtIn, &vtOut, BytesReturned, &status); RmpSetMonitorState(RmonIdle, NULL); RmpRemoveDeadlockMonitorList( pDueTimeEntry ); SysFreeString (pbResourceTypeName) ; if (FAILED(hr)) { CL_LOGFAILURE(hr); // Use the default processing status = ERROR_INVALID_FUNCTION; } } #endif if ( status == ERROR_INVALID_FUNCTION ) { switch ( ControlCode ) { case CLUSCTL_RESOURCE_TYPE_GET_COMMON_RESOURCE_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( RmpResourceTypeCommonProperties, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_TYPE_GET_CLASS_INFO: if ( OutBufferSize < sizeof(CLUS_RESOURCE_CLASS_INFO) ) { *BytesReturned = 0; *Required = sizeof(CLUS_RESOURCE_CLASS_INFO); if ( OutBuffer == NULL ) { status = ERROR_SUCCESS; } else { status = ERROR_MORE_DATA; } } else { PCLUS_RESOURCE_CLASS_INFO ptrResClassInfo = (PCLUS_RESOURCE_CLASS_INFO) OutBuffer; ptrResClassInfo->rc = CLUS_RESCLASS_UNKNOWN; ptrResClassInfo->SubClass = 0; *BytesReturned = sizeof(CLUS_RESOURCE_CLASS_INFO); status = ERROR_SUCCESS; } break; case CLUSCTL_RESOURCE_TYPE_GET_REQUIRED_DEPENDENCIES: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_TYPE_GET_CHARACTERISTICS: if ( OutBufferSize < sizeof(DWORD) ) { *BytesReturned = 0; *Required = sizeof(DWORD); if ( OutBuffer == NULL ) { status = ERROR_SUCCESS; } else { status = ERROR_MORE_DATA; } } else { *BytesReturned = sizeof(DWORD); *(LPDWORD)OutBuffer = characteristics; status = ERROR_SUCCESS; } break; case CLUSCTL_RESOURCE_TYPE_GET_FLAGS: status = RmpResourceTypeGetFlags( ResourceTypeName, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_TYPE_GET_REGISTRY_CHECKPOINTS: *BytesReturned = 0; *Required = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_TYPE_ENUM_COMMON_PROPERTIES: status = RmpResourceTypeEnumCommonProperties( ResourceTypeName, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_TYPE_GET_RO_COMMON_PROPERTIES: status = RmpResourceTypeGetCommonProperties( ResourceTypeName, TRUE, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_TYPE_GET_COMMON_PROPERTIES: status = RmpResourceTypeGetCommonProperties( ResourceTypeName, FALSE, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_TYPE_VALIDATE_COMMON_PROPERTIES: status = RmpResourceTypeValidateCommonProperties( ResourceTypeName, InBuffer, InBufferSize ); break; case CLUSCTL_RESOURCE_TYPE_SET_COMMON_PROPERTIES: status = RmpResourceTypeSetCommonProperties( ResourceTypeName, InBuffer, InBufferSize ); break; case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES: status = RmpResourceTypeEnumPrivateProperties( ResourceTypeName, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_TYPE_GET_RO_PRIVATE_PROPERTIES: if ( OutBufferSize < sizeof(DWORD) ) { *BytesReturned = 0; *Required = sizeof(DWORD); if ( OutBuffer == NULL ) { status = ERROR_SUCCESS; } else { status = ERROR_MORE_DATA; } } else { LPDWORD ptrDword = (LPDWORD) OutBuffer; *ptrDword = 0; *BytesReturned = sizeof(DWORD); status = ERROR_SUCCESS; } break; case CLUSCTL_RESOURCE_TYPE_GET_PRIVATE_PROPERTIES: status = RmpResourceTypeGetPrivateProperties( ResourceTypeName, OutBuffer, OutBufferSize, BytesReturned, Required ); break; case CLUSCTL_RESOURCE_TYPE_VALIDATE_PRIVATE_PROPERTIES: status = RmpResourceTypeValidatePrivateProperties( ResourceTypeName, InBuffer, InBufferSize ); break; case CLUSCTL_RESOURCE_TYPE_SET_PRIVATE_PROPERTIES: status = RmpResourceTypeSetPrivateProperties( ResourceTypeName, InBuffer, InBufferSize ); break; default: break; } } else { // If the function is returning a buffer size without // copying data, move this info around to satisfy RPC. if ( *BytesReturned > OutBufferSize ) { *Required = *BytesReturned; *BytesReturned = 0; } if ( (status == ERROR_MORE_DATA) && (OutBuffer == NULL) ) { status = ERROR_SUCCESS; } } FnExit: if (ResDllFnInfo.hDll) FreeLibrary(ResDllFnInfo.hDll); #ifdef COMRES if (ResDllInterfaces.pClusterResource) IClusterResource_Release (ResDllInterfaces.pClusterResource) ; if (ResDllInterfaces.pClusterQuorumResource) IClusterQuorumResource_Release (ResDllInterfaces.pClusterQuorumResource) ; if (ResDllInterfaces.pClusterResControl) IClusterResControl_Release ( ResDllInterfaces.pClusterResControl ) ; #endif return(status); } // RmResourceTypeControl LPWSTR GetParameter( IN HKEY ClusterKey, IN LPCWSTR ValueName ) /*++ Routine Description: Reads a REG_SZ parameter from the cluster regitry, and allocates the necessary storage for it. Arguments: ClusterKey - supplies the cluster key where the parameter is stored. ValueName - supplies the name of the value. Return Value: A pointer to a buffer containing the parameter value on success. NULL on failure. --*/ { LPWSTR value; DWORD valueLength; DWORD valueType; DWORD status; valueLength = 0; status = ClusterRegQueryValue( ClusterKey, ValueName, &valueType, NULL, &valueLength ); if ( (status != ERROR_SUCCESS) && (status != ERROR_MORE_DATA) ) { SetLastError(status); return(NULL); } if ( valueType == REG_SZ ) { valueLength += sizeof(UNICODE_NULL); } value = LocalAlloc(LMEM_FIXED, valueLength); if ( value == NULL ) { return(NULL); } status = ClusterRegQueryValue(ClusterKey, ValueName, &valueType, (LPBYTE)value, &valueLength); if ( status != ERROR_SUCCESS) { LocalFree(value); SetLastError(status); value = NULL; } return(value); } // GetParameter error_status_t s_RmUpdateDeadlockDetectionParams( IN handle_t IDL_handle, IN DWORD dwDeadlockDetectionTimeout ) /*++ Routine Description: Changes the poll intervals defined for a resource. Arguments: IDL_handle - Unused. dwDeadlockDetectionTimeout - Deadlock detection timeout. Return Value: ERROR_SUCCESS if successful Win32 error otherwise --*/ { DWORD dwStatus = ERROR_SUCCESS; // // Use the default for deadlock timeout if 0 is passed in. // dwDeadlockDetectionTimeout = ( dwDeadlockDetectionTimeout == 0 ) ? CLUSTER_RESOURCE_DLL_DEFAULT_DEADLOCK_TIMEOUT_SECS : dwDeadlockDetectionTimeout; // // Initialize the deadlock monitoring subsystem if it is not already initialized. // dwStatus = RmpDeadlockMonitorInitialize ( dwDeadlockDetectionTimeout ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] s_RmUpdateDeadlockDetectionParams: Unable to initialize the deadlock monitor, error %1!u!.\n", dwStatus); goto FnExit; } dwStatus = RmpUpdateDeadlockDetectionParams ( dwDeadlockDetectionTimeout ); if ( dwStatus != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_CRITICAL, "[RM] s_RmUpdateDeadlockDetectionParams: Unable to update the parameters for deadlock monitor, error %1!u!.\n", dwStatus); goto FnExit; } FnExit: return ( dwStatus ); } // s_RmUpdateDeadlockDetectionParams #ifdef COMRES RESID Resmon_Open ( IN PRESOURCE Resource, IN HKEY ResourceKey ) { if (Resource->dwType == RESMON_TYPE_DLL) { return (Resource->pOpen) (Resource->ResourceName, ResourceKey, Resource) ; } else { HRESULT hr ; OLERESID ResId = 0 ; BSTR pbResourceName = SysAllocString (Resource->ResourceName) ; if (pbResourceName == NULL) { SetLastError ( ERROR_NOT_ENOUGH_MEMORY) ; CL_LOGFAILURE( ERROR_NOT_ENOUGH_MEMORY) ; goto ErrorExit ; } hr = IClusterResource_Open(Resource->pClusterResource, pbResourceName, (OLEHKEY)ResourceKey, (OLERESOURCE_HANDLE)Resource, &ResId); SysFreeString (pbResourceName) ; if (FAILED(hr)) { SetLastError (hr) ; CL_LOGFAILURE(hr); } ErrorExit: return (RESID) ResId ; } } VOID Resmon_Close ( IN PRESOURCE Resource ) { if (Resource->dwType == RESMON_TYPE_DLL) { (Resource->pClose) (Resource->Id) ; } else { HRESULT hr ; hr = IClusterResource_Close (Resource->pClusterResource, (OLERESID)Resource->Id); if (FAILED(hr)) { SetLastError (hr) ; CL_LOGFAILURE(hr); } } } DWORD Resmon_Online ( IN PRESOURCE Resource, IN OUT LPHANDLE EventHandle ) { if (Resource->dwType == RESMON_TYPE_DLL) { return (Resource->pOnline) (Resource->Id, EventHandle) ; } else { HRESULT hr ; long lRet ; hr = IClusterResource_Online(Resource->pClusterResource, (OLERESID)Resource->Id, (LPOLEHANDLE)EventHandle, &lRet); if (FAILED(hr)) { SetLastError (lRet = hr) ; // Return a error CL_LOGFAILURE(hr); } return lRet ; } } DWORD Resmon_Offline ( IN PRESOURCE Resource ) { if (Resource->dwType == RESMON_TYPE_DLL) { return (Resource->pOffline) (Resource->Id) ; } else { HRESULT hr ; long lRet ; hr = IClusterResource_Offline(Resource->pClusterResource, (OLERESID)Resource->Id, &lRet); if (FAILED(hr)) { SetLastError (lRet = hr) ; // Return a error CL_LOGFAILURE(hr); } return lRet ; } } VOID Resmon_Terminate ( IN PRESOURCE Resource ) { if (Resource->dwType == RESMON_TYPE_DLL) { (Resource->pTerminate) (Resource->Id) ; } else { HRESULT hr ; hr = IClusterResource_Terminate (Resource->pClusterResource, (OLERESID)Resource->Id); if (FAILED(hr)) { SetLastError (hr) ; CL_LOGFAILURE(hr); } } } BOOL Resmon_LooksAlive ( IN PRESOURCE Resource ) { if (Resource->dwType == RESMON_TYPE_DLL) { return (Resource->pLooksAlive) (Resource->Id) ; } else { HRESULT hr ; long lRet ; hr = IClusterResource_LooksAlive (Resource->pClusterResource, (OLERESID)Resource->Id, &lRet); if (FAILED(hr)) { SetLastError (hr) ; CL_LOGFAILURE(hr); lRet = 0 ; // Incase of failure return 0 to indicate LooksAlive is failed. } return lRet ; } } BOOL Resmon_IsAlive ( IN PRESOURCE Resource ) { if (Resource->dwType == RESMON_TYPE_DLL) { return (Resource->pIsAlive) (Resource->Id) ; } else { HRESULT hr ; long lRet ; hr = IClusterResource_IsAlive (Resource->pClusterResource, (OLERESID)Resource->Id, &lRet); if (FAILED(hr)) { SetLastError (hr) ; CL_LOGFAILURE(hr); lRet = 0 ; // Incase of failure return 0 to indicate IsAlive is failed. } return lRet ; } } DWORD Resmon_Arbitrate ( IN PRESOURCE Resource, IN PQUORUM_RESOURCE_LOST LostQuorumResource ) { if (Resource->dwType == RESMON_TYPE_DLL) { return (Resource->pArbitrate) (Resource->Id, LostQuorumResource) ; } else { HRESULT hr ; long lRet ; hr = IClusterQuorumResource_QuorumArbitrate(Resource->pClusterQuorumResource, (OLERESID)Resource->Id, (POLEQUORUM_RESOURCE_LOST)LostQuorumResource, &lRet); if (FAILED(hr)) { SetLastError (lRet = hr) ; CL_LOGFAILURE(hr); } return lRet ; } } DWORD Resmon_Release ( IN PRESOURCE Resource ) { if (Resource->dwType == RESMON_TYPE_DLL) { return (Resource->pRelease) (Resource->Id) ; } else { HRESULT hr ; long lRet ; hr = IClusterQuorumResource_QuorumRelease(Resource->pClusterQuorumResource, (OLERESID)Resource->Id, &lRet); if (FAILED(hr)) { SetLastError (lRet = hr) ; CL_LOGFAILURE(hr); } return lRet ; } } DWORD Resmon_ResourceControl ( IN PRESOURCE Resource, IN DWORD ControlCode, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) { if (Resource->dwType == RESMON_TYPE_DLL) { return (Resource->pResourceControl)( Resource->Id, ControlCode, InBuffer, InBufferSize, OutBuffer, OutBufferSize, BytesReturned ); } else { long status ; HRESULT hr ; VARIANT vtIn, vtOut ; SAFEARRAY sfIn = {1, 0, 1, 0, InBuffer, {InBufferSize, 0} } ; SAFEARRAY sfOut = {1, FADF_FIXEDSIZE, 1, 0, OutBuffer, {OutBufferSize, 0} } ; SAFEARRAY *psfOut = &sfOut ; vtIn.vt = VT_ARRAY | VT_UI1 ; vtOut.vt = VT_ARRAY | VT_UI1 | VT_BYREF ; vtIn.parray = &sfIn ; vtOut.pparray = &psfOut ; hr = IClusterResControl_ResourceControl ( Resource->pClusterResControl, (OLERESID)Resource->Id, (long)ControlCode, &vtIn, &vtOut, (long *)BytesReturned, &status); if (FAILED(hr)) { CL_LOGFAILURE(hr); // Use the default processing status = ERROR_INVALID_FUNCTION; } return (DWORD)status ; } } #endif // COMRES