///////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1998 Microsoft Corporation // // Module Name: // // Dummy.cpp // // Abstract: // // Resource DLL for Dummy (Dummy). // // Author: // // Galen Barbee (galenb) Sept 03, 1998 // // Revision History: // // Notes: // ///////////////////////////////////////////////////////////////////////////// #pragma comment(lib, "clusapi.lib") #pragma comment(lib, "resutils.lib") #define UNICODE 1 #pragma warning( disable : 4115 ) // named type definition in parentheses #pragma warning( disable : 4201 ) // nonstandard extension used : nameless struct/union #pragma warning( disable : 4214 ) // nonstandard extension used : bit field types other than int #include #pragma warning( default : 4214 ) // nonstandard extension used : bit field types other than int #pragma warning( default : 4201 ) // nonstandard extension used : nameless struct/union #pragma warning( default : 4115 ) // named type definition in parentheses #include #include #include // // Type and constant definitions. // #define DUMMY_RESNAME L"Dummy" #define DBG_PRINT printf #define MAX_WAIT (10000) // wait for 10 seconds #define DUMMY_FLAG_VALID 0x00000001 #define DUMMY_FLAG_ASYNC 0x00000002 // Asynchronous failure mode #define DUMMY_FLAG_PENDING 0x00000004 // Pending mode on shutdown #define AsyncMode(Resource) (Resource->Flags & DUMMY_FLAG_ASYNC) #define PendingMode(Resource) (Resource->Flags & DUMMY_FLAG_PENDING) #define EnterAsyncMode(Resource) (Resource->Flags |= DUMMY_FLAG_ASYNC) #define DummyAcquireResourceLock(_res) EnterCriticalSection(&((_res)->Lock)) #define DummyReleaseResourceLock(_res) LeaveCriticalSection(&((_res)->Lock)) #define DummyAcquireGlobalLock() \ { \ DWORD status; \ status = WaitForSingleObject( DummyGlobalMutex, INFINITE ); \ } #define DummyReleaseGlobalLock() \ { \ BOOLEAN released; \ released = ReleaseMutex( DummyGlobalMutex ); \ } // // ADDPARAM: Add new parameters here. // #define PARAM_NAME__PENDING L"Pending" #define PARAM_NAME__PENDTIME L"PendTime" #define PARAM_NAME__OPENSFAIL L"OpensFail" #define PARAM_NAME__FAILED L"Failed" #define PARAM_NAME__ASYNCHRONOUS L"Asynchronous" #define PARAM_MIN__PENDING (0) #define PARAM_MAX__PENDING (1) #define PARAM_DEFAULT__PENDING (0) #define PARAM_MIN__PENDTIME (0) #define PARAM_MAX__PENDTIME (4294967295) #define PARAM_DEFAULT__PENDTIME (0) #define PARAM_MIN__OPENSFAIL (0) #define PARAM_MAX__OPENSFAIL (1) #define PARAM_DEFAULT__OPENSFAIL (0) #define PARAM_MIN__FAILED (0) #define PARAM_MAX__FAILED (1) #define PARAM_DEFAULT__FAILED (0) #define PARAM_MIN__ASYNCHRONOUS (0) #define PARAM_MAX__ASYNCHRONOUS (1) #define PARAM_DEFAULT__ASYNCHRONOUS (0) typedef enum TimerType { TimerNotUsed = 0, TimerErrorPending, TimerOnlinePending, TimerOfflinePending }; // // ADDPARAM: Add new parameters here. // typedef struct _DUMMY_PARAMS { DWORD Pending; DWORD PendTime; DWORD OpensFail; DWORD Failed; DWORD Asynchronous; } DUMMY_PARAMS, *PDUMMY_PARAMS; typedef struct _DUMMY_RESOURCE { RESID ResId; // for validation DUMMY_PARAMS Params; HKEY ParametersKey; RESOURCE_HANDLE ResourceHandle; LPWSTR ResourceName; CLUS_WORKER OnlineThread; CLUS_WORKER OfflineThread; CLUSTER_RESOURCE_STATE State; DWORD Flags; HANDLE SignalEvent; HANDLE TimerThreadWakeup; DWORD TimerType; CRITICAL_SECTION Lock; } DUMMY_RESOURCE, *PDUMMY_RESOURCE; // // Global data. // // Sync Mutex HANDLE DummyGlobalMutex = NULL; // Event Logging routine. PLOG_EVENT_ROUTINE g_LogEvent = NULL; // Resource Status routine for pending Online and Offline calls. PSET_RESOURCE_STATUS_ROUTINE g_SetResourceStatus = NULL; // Forward reference to our RESAPI function table. extern CLRES_FUNCTION_TABLE g_DummyFunctionTable; // // Dummy resource read-write private properties. // RESUTIL_PROPERTY_ITEM DummyResourcePrivateProperties[] = { { PARAM_NAME__PENDING, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__PENDING, PARAM_MIN__PENDING, PARAM_MAX__PENDING, 0, FIELD_OFFSET(DUMMY_PARAMS,Pending) }, { PARAM_NAME__PENDTIME, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__PENDTIME, PARAM_MIN__PENDTIME, PARAM_MAX__PENDTIME, 0, FIELD_OFFSET(DUMMY_PARAMS,PendTime) }, { PARAM_NAME__OPENSFAIL, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__OPENSFAIL, PARAM_MIN__OPENSFAIL, PARAM_MAX__OPENSFAIL, 0, FIELD_OFFSET(DUMMY_PARAMS,OpensFail) }, { PARAM_NAME__FAILED, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__FAILED, PARAM_MIN__FAILED, PARAM_MAX__FAILED, 0, FIELD_OFFSET(DUMMY_PARAMS,Failed) }, { PARAM_NAME__ASYNCHRONOUS, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__ASYNCHRONOUS, PARAM_MIN__ASYNCHRONOUS, PARAM_MAX__ASYNCHRONOUS, 0, FIELD_OFFSET(DUMMY_PARAMS,Asynchronous) }, { 0 } }; // // Function prototypes. // DWORD WINAPI Startup( IN LPCWSTR ResourceType, IN DWORD MinVersionSupported, IN DWORD MaxVersionSupported, IN PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus, IN PLOG_EVENT_ROUTINE LogEvent, OUT PCLRES_FUNCTION_TABLE *FunctionTable ); RESID WINAPI DummyOpen( IN LPCWSTR ResourceName, IN HKEY ResourceKey, IN RESOURCE_HANDLE ResourceHandle ); void WINAPI DummyClose( IN RESID ResourceId ); DWORD WINAPI DummyOnline( IN RESID ResourceId, IN OUT PHANDLE EventHandle ); DWORD WINAPI DummyOnlineThread( IN PCLUS_WORKER WorkerPtr, IN PDUMMY_RESOURCE ResourceEntry ); DWORD WINAPI DummyOffline( IN RESID ResourceId ); DWORD WINAPI DummyOfflineThread( PCLUS_WORKER WorkerPtr, IN PDUMMY_RESOURCE ResourceEntry ); void WINAPI DummyTerminate( IN RESID ResourceId ); DWORD DummyDoTerminate( IN PDUMMY_RESOURCE ResourceEntry ); BOOL WINAPI DummyLooksAlive( IN RESID ResourceId ); BOOL WINAPI DummyIsAlive( IN RESID ResourceId ); BOOL DummyCheckIsAlive( IN PDUMMY_RESOURCE ResourceEntry ); DWORD WINAPI DummyResourceControl( IN RESID ResourceId, IN DWORD ControlCode, IN void * InBuffer, IN DWORD InBufferSize, OUT void * OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ); DWORD DummyGetPrivateResProperties( IN OUT PDUMMY_RESOURCE ResourceEntry, OUT void * OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ); DWORD DummyValidatePrivateResProperties( IN OUT PDUMMY_RESOURCE ResourceEntry, IN const PVOID InBuffer, IN DWORD InBufferSize, OUT PDUMMY_PARAMS Params ); DWORD DummySetPrivateResProperties( IN OUT PDUMMY_RESOURCE ResourceEntry, IN const PVOID InBuffer, IN DWORD InBufferSize ); DWORD DummyTimerThread( IN PDUMMY_RESOURCE ResourceEntry, IN PCLUS_WORKER WorkerPtr ); ///////////////////////////////////////////////////////////////////////////// //++ // // DummyInit // // Routine Description: // // Process attach initialization routine. // // Arguments: // // None. // // Return Value: // // TRUE if initialization succeeded. FALSE otherwise. // //-- ///////////////////////////////////////////////////////////////////////////// static BOOLEAN DummyInit( void ) { DummyGlobalMutex = CreateMutex( NULL, FALSE, NULL ); return DummyGlobalMutex != NULL; } //*** DummyInit() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyCleanup // // Routine Description: // // Process detach cleanup routine. // // Arguments: // // None. // // Return Value: // // None. // //-- ///////////////////////////////////////////////////////////////////////////// static void DummyCleanup( void ) { if ( DummyGlobalMutex != NULL ) { CloseHandle( DummyGlobalMutex ); DummyGlobalMutex = NULL; } return; } //*** DummyCleanup() ///////////////////////////////////////////////////////////////////////////// //++ // // DllMain // // Routine Description: // // Main DLL entry point. // // Arguments: // // DllHandle - DLL instance handle. // // Reason - Reason for being called. // // Reserved - Reserved argument. // // Return Value: // // TRUE - Success. // // FALSE - Failure. // //-- ///////////////////////////////////////////////////////////////////////////// BOOLEAN WINAPI DllMain( IN HINSTANCE DllHandle, IN DWORD Reason, IN void * //Reserved ) { BOOLEAN bRet = TRUE; switch( Reason ) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls( DllHandle ); bRet = DummyInit(); break; case DLL_PROCESS_DETACH: DummyCleanup(); break; } return bRet; } //*** DllMain() ///////////////////////////////////////////////////////////////////////////// //++ // // Startup // // Routine Description: // // Startup the resource DLL. This routine verifies that at least one // currently supported version of the resource DLL is between // MinVersionSupported and MaxVersionSupported. If not, then the resource // DLL should return ERROR_REVISION_MISMATCH. // // If more than one version of the resource DLL interface is supported by // the resource DLL, then the highest version (up to MaxVersionSupported) // should be returned as the resource DLL's interface. If the returned // version is not within range, then startup fails. // // The ResourceType is passed in so that if the resource DLL supports more // than one ResourceType, it can pass back the correct function table // associated with the ResourceType. // // Arguments: // // ResourceType - The type of resource requesting a function table. // // MinVersionSupported - The minimum resource DLL interface version // supported by the cluster software. // // MaxVersionSupported - The maximum resource DLL interface version // supported by the cluster software. // // SetResourceStatus - Pointer to a routine that the resource DLL should // call to update the state of a resource after the Online or Offline // routine returns a status of ERROR_IO_PENDING. // // LogEvent - Pointer to a routine that handles the reporting of events // from the resource DLL. // // FunctionTable - Returns a pointer to the function table defined for the // version of the resource DLL interface returned by the resource DLL. // // Return Value: // // ERROR_SUCCESS - The operation was successful. // // ERROR_MOD_NOT_FOUND - The resource type is unknown by this DLL. // // ERROR_REVISION_MISMATCH - The version of the cluster service doesn't // match the versrion of the DLL. // // Win32 error code - The operation failed. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD WINAPI Startup( IN LPCWSTR ResourceType, IN DWORD MinVersionSupported, IN DWORD MaxVersionSupported, IN PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus, IN PLOG_EVENT_ROUTINE LogEvent, OUT PCLRES_FUNCTION_TABLE * FunctionTable ) { if ( ( MinVersionSupported > CLRES_VERSION_V1_00 ) || ( MaxVersionSupported < CLRES_VERSION_V1_00 ) ) { return ERROR_REVISION_MISMATCH; } if ( lstrcmpiW( ResourceType, DUMMY_RESNAME ) != 0 ) { return ERROR_MOD_NOT_FOUND; } if ( g_LogEvent == NULL ) { g_LogEvent = LogEvent; g_SetResourceStatus = SetResourceStatus; } if ( FunctionTable != NULL ) { *FunctionTable = &g_DummyFunctionTable; } return ERROR_SUCCESS; } //*** Startup() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyOpen // // Routine Description: // // Open routine for Dummy resources. // // Open the specified resource (create an instance of the resource). // Allocate all structures necessary to bring the specified resource // online. // // Arguments: // // ResourceName - Supplies the name of the resource to open. // // ResourceKey - Supplies handle to the resource's cluster configuration // database key. // // ResourceHandle - A handle that is passed back to the resource monitor // when the SetResourceStatus or LogEvent method is called. See the // description of the SetResourceStatus and LogEvent methods on the // DummyStatup routine. This handle should never be closed or used // for any purpose other than passing it as an argument back to the // Resource Monitor in the SetResourceStatus or LogEvent callback. // // Return Value: // // RESID of created resource. // // NULL on failure. // //-- ///////////////////////////////////////////////////////////////////////////// RESID WINAPI DummyOpen( IN LPCWSTR ResourceName, IN HKEY ResourceKey, IN RESOURCE_HANDLE ResourceHandle ) { DWORD status; RESID resid = 0; HKEY parametersKey = NULL; PDUMMY_RESOURCE ResourceEntry = NULL; LPWSTR nameOfPropInError; // // Open the Parameters registry key for this resource. // status = ClusterRegOpenKey( ResourceKey, L"Parameters", KEY_ALL_ACCESS, ¶metersKey ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to open Parameters key. Error: %1!u!.\n", status ); goto exit; } // // Allocate a resource entry. // ResourceEntry = (PDUMMY_RESOURCE)LocalAlloc( LMEM_ZEROINIT, sizeof( DUMMY_RESOURCE ) ); if ( ResourceEntry == NULL ) { status = GetLastError(); (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to allocate resource entry structure. Error: %1!u!.\n", status ); goto exit; } // // Initialize the resource entry.. // ZeroMemory( ResourceEntry, sizeof( DUMMY_RESOURCE ) ); ResourceEntry->ResId = (RESID)ResourceEntry; // for validation ResourceEntry->ResourceHandle = ResourceHandle; ResourceEntry->ParametersKey = parametersKey; ResourceEntry->State = ClusterResourceOffline; InitializeCriticalSection( &( ResourceEntry->Lock ) ); // // Save the name of the resource. // ResourceEntry->ResourceName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT, (lstrlenW( ResourceName ) + 1 ) * sizeof( WCHAR ) ); if ( ResourceEntry->ResourceName == NULL ) { goto exit; } lstrcpyW( ResourceEntry->ResourceName, ResourceName ); // // Startup for the resource. // // TODO: Add your resource startup code here. // // Read parameters. // status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ParametersKey, DummyResourcePrivateProperties, (LPBYTE)&ResourceEntry->Params, FALSE, // CheckForRequiredProperties &nameOfPropInError ); if ( status == ERROR_SUCCESS ) { if ( ResourceEntry->Params.OpensFail ) { goto exit; } else { resid = (RESID)ResourceEntry; } } else { goto exit; } // // Create a TimerThreadWakeup event // ResourceEntry->TimerThreadWakeup = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( ResourceEntry->TimerThreadWakeup == NULL ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to create timer thread wakeup event\n" ); resid = 0; goto exit; } if ( ResourceEntry->Params.Pending ) { ResourceEntry->Flags |= DUMMY_FLAG_PENDING; } if ( ResourceEntry->Params.Asynchronous ) { EnterAsyncMode( ResourceEntry ); ResourceEntry->SignalEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( ResourceEntry->SignalEvent == NULL ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to create a timer event\n"); resid = 0; goto exit; } } exit: if ( resid == 0 ) { if ( parametersKey != NULL ) { ClusterRegCloseKey( parametersKey ); } if ( ResourceEntry != NULL ) { LocalFree( ResourceEntry->ResourceName ); LocalFree( ResourceEntry ); } } if ( status != ERROR_SUCCESS ) { SetLastError( status ); } return resid; } //*** DummyOpen() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyClose // // Routine Description: // // Close routine for Dummy resources. // // Close the specified resource and deallocate all structures, etc., // allocated in the Open call. If the resource is not in the offline state, // then the resource should be taken offline (by calling Terminate) before // the close operation is performed. // // Arguments: // // ResourceId - Supplies the RESID of the resource to close. // // Return Value: // // None. //-- ///////////////////////////////////////////////////////////////////////////// void WINAPI DummyClose( IN RESID ResourceId ) { PDUMMY_RESOURCE ResourceEntry; ResourceEntry = (PDUMMY_RESOURCE)ResourceId; if ( ResourceEntry == NULL ) { DBG_PRINT( "Dummy: Close request for a nonexistent resource id 0x%p\n", ResourceId ); return; } if ( ResourceEntry->ResId != ResourceId ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Close resource sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return; } #ifdef LOG_VERBOSE (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Close request.\n" ); #endif DeleteCriticalSection( &( ResourceEntry->Lock ) ); if ( ResourceEntry->TimerThreadWakeup != NULL ) { CloseHandle( ResourceEntry->TimerThreadWakeup ); } if ( ResourceEntry->SignalEvent != NULL ) { CloseHandle( ResourceEntry->SignalEvent ); } // // Close the Parameters key. // if ( ResourceEntry->ParametersKey ) { ClusterRegCloseKey( ResourceEntry->ParametersKey ); } // // Deallocate the resource entry. // // ADDPARAM: Add new parameters here. LocalFree( ResourceEntry->ResourceName ); LocalFree( ResourceEntry ); return; } //*** DummyClose() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyOnline // // Routine Description: // // Online routine for Dummy resources. // // Bring the specified resource online (available for use). The resource // DLL should attempt to arbitrate for the resource if it is present on a // shared medium, like a shared SCSI bus. // // Arguments: // // ResourceId - Supplies the resource id for the resource to be brought // online (available for use). // // EventHandle - Returns a signalable handle that is signaled when the // resource DLL detects a failure on the resource. This argument is // NULL on input, and the resource DLL returns NULL if asynchronous // notification of failures is not supported, otherwise this must be // the address of a handle that is signaled on resource failures. // // Return Value: // // ERROR_SUCCESS - The operation was successful, and the resource is now // online. // // ERROR_RESOURCE_NOT_FOUND - RESID is not valid. // // ERROR_RESOURCE_NOT_AVAILABLE - If the resource was arbitrated with some // other systems and one of the other systems won the arbitration. // // ERROR_IO_PENDING - The request is pending, a thread has been activated // to process the online request. The thread that is processing the // online request will periodically report status by calling the // SetResourceStatus callback method, until the resource is placed into // the ClusterResourceOnline state (or the resource monitor decides to // timeout the online request and Terminate the resource. This pending // timeout value is settable and has a default value of 3 minutes.). // // Win32 error code - The operation failed. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD WINAPI DummyOnline( IN RESID ResourceId, IN OUT PHANDLE //EventHandle ) { PDUMMY_RESOURCE ResourceEntry = NULL; DWORD status; ResourceEntry = (PDUMMY_RESOURCE)ResourceId; if ( ResourceEntry == NULL ) { DBG_PRINT( "Dummy: Online request for a nonexistent resource id 0x%p.\n", ResourceId ); return ERROR_RESOURCE_NOT_FOUND; } if ( ResourceEntry->ResId != ResourceId ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Online service sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return ERROR_RESOURCE_NOT_FOUND; } DummyAcquireResourceLock( ResourceEntry ); #ifdef LOG_VERBOSE (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Online request.\n" ); #endif ResourceEntry->State = ClusterResourceOffline; ClusWorkerTerminate( &ResourceEntry->OnlineThread ); ClusWorkerTerminate( &ResourceEntry->OfflineThread ); status = ClusWorkerCreate( &ResourceEntry->OnlineThread, (PWORKER_START_ROUTINE)DummyOnlineThread, ResourceEntry ); if ( status != ERROR_SUCCESS ) { ResourceEntry->State = ClusterResourceFailed; (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Online: Unable to start thread, status %1!u!.\n", status ); } else { status = ERROR_IO_PENDING; } DummyReleaseResourceLock( ResourceEntry ); return status; } //*** DummyOnline() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyOnlineThread // // Routine Description: // // Worker function which brings a resource from the resource table online. // This function is executed in a separate thread. // // Arguments: // // WorkerPtr - Supplies the worker structure // // ResourceEntry - A pointer to the DUMMY_RESOURCE block for this resource. // // Return Value: // // ERROR_SUCCESS - The operation completed successfully. // // Win32 error code - The operation failed. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD WINAPI DummyOnlineThread( IN PCLUS_WORKER WorkerPtr, IN PDUMMY_RESOURCE ResourceEntry ) { RESOURCE_STATUS resourceStatus; DWORD status = ERROR_SUCCESS; LPWSTR nameOfPropInError; DummyAcquireResourceLock( ResourceEntry ); ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; resourceStatus.WaitHint = 0; resourceStatus.CheckPoint = 1; // // Read parameters. // status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ParametersKey, DummyResourcePrivateProperties, (LPBYTE)&ResourceEntry->Params, TRUE, // CheckForRequiredProperties &nameOfPropInError ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to read the '%1' property. Error: %2!u!.\n", (nameOfPropInError == NULL ? L"" : nameOfPropInError), status ); goto exit; } // // Bring the resource online. // if ( ResourceEntry->Params.Pending ) { ResourceEntry->Flags |= DUMMY_FLAG_PENDING; ResourceEntry->TimerType = TimerOnlinePending; status = DummyTimerThread( ResourceEntry, WorkerPtr ); } exit: if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Error %1!u! bringing resource online.\n", status ); } else { resourceStatus.ResourceState = ClusterResourceOnline; } // _ASSERTE(g_SetResourceStatus != NULL); g_SetResourceStatus( ResourceEntry->ResourceHandle, &resourceStatus ); ResourceEntry->State = resourceStatus.ResourceState; DummyReleaseResourceLock( ResourceEntry ); return status; } //*** DummyOnlineThread() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyOffline // // Routine Description: // // Offline routine for Dummy resources. // // Take the specified resource offline gracefully (unavailable for use). // Wait for any cleanup operations to complete before returning. // // Arguments: // // ResourceId - Supplies the resource id for the resource to be shutdown // gracefully. // // Return Value: // // ERROR_SUCCESS - The request completed successfully and the resource is // offline. // // ERROR_RESOURCE_NOT_FOUND - RESID is not valid. // // ERROR_IO_PENDING - The request is still pending, a thread has been // activated to process the offline request. The thread that is // processing the offline will periodically report status by calling // the SetResourceStatus callback method, until the resource is placed // into the ClusterResourceOffline state (or the resource monitor decides // to timeout the offline request and Terminate the resource). // // Win32 error code - Will cause the resource monitor to log an event and // call the Terminate routine. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD WINAPI DummyOffline( IN RESID ResourceId ) { PDUMMY_RESOURCE ResourceEntry; DWORD status = ERROR_SUCCESS; ResourceEntry = (PDUMMY_RESOURCE)ResourceId; if ( ResourceEntry == NULL ) { DBG_PRINT( "Dummy: Offline request for a nonexistent resource id 0x%p\n", ResourceId ); return ERROR_RESOURCE_NOT_FOUND; } if ( ResourceEntry->ResId != ResourceId ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Offline resource sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return ERROR_RESOURCE_NOT_FOUND; } DummyAcquireResourceLock( ResourceEntry ); #ifdef LOG_VERBOSE (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Offline request.\n" ); #endif // TODO: Offline code // NOTE: Offline should try to shut the resource down gracefully, whereas // Terminate must shut the resource down immediately. If there are no // differences between a graceful shut down and an immediate shut down, // Terminate can be called for Offline, as it is below. However, if there // are differences, replace the call to Terminate below with your graceful // shutdown code. ResourceEntry->State = ClusterResourceOnline; ClusWorkerTerminate( &ResourceEntry->OfflineThread ); ClusWorkerTerminate( &ResourceEntry->OnlineThread ); status = ClusWorkerCreate( &ResourceEntry->OfflineThread, (PWORKER_START_ROUTINE)DummyOfflineThread, ResourceEntry ); if ( status != ERROR_SUCCESS ) { ResourceEntry->State = ClusterResourceFailed; (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Online: Unable to start thread, status %1!u!.\n", status ); } else { status = ERROR_IO_PENDING; } DummyReleaseResourceLock( ResourceEntry ); return status; } //*** DummyOffline() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyOfflineThread // // Routine Description: // // Worker function which brings a resource from the resource table online. // This function is executed in a separate thread. // // Arguments: // // WorkerPtr - Supplies the worker structure // // ResourceEntry - A pointer to the DUMMY_RESOURCE block for this resource. // // Return Value: // // ERROR_SUCCESS - The operation completed successfully. // // Win32 error code - The operation failed. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD WINAPI DummyOfflineThread( IN PCLUS_WORKER WorkerPtr, IN PDUMMY_RESOURCE ResourceEntry ) { RESOURCE_STATUS resourceStatus; DWORD status = ERROR_SUCCESS; LPWSTR nameOfPropInError; DummyAcquireResourceLock( ResourceEntry ); ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; resourceStatus.WaitHint = 0; resourceStatus.CheckPoint = 1; // // Read parameters. // status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ParametersKey, DummyResourcePrivateProperties, (LPBYTE)&ResourceEntry->Params, FALSE, // CheckForRequiredProperties &nameOfPropInError ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to read the '%1' property. Error: %2!u!.\n", (nameOfPropInError == NULL ? L"" : nameOfPropInError), status ); goto exit; } // // Bring the resource online. // if ( ResourceEntry->Params.Pending ) { ResourceEntry->Flags |= DUMMY_FLAG_PENDING; ResourceEntry->TimerType = TimerOfflinePending; status = DummyTimerThread( ResourceEntry, WorkerPtr ); } exit: if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Error %1!u! bringing resource online.\n", status ); } else { resourceStatus.ResourceState = ClusterResourceOffline; } // _ASSERTE(g_SetResourceStatus != NULL); g_SetResourceStatus( ResourceEntry->ResourceHandle, &resourceStatus ); ResourceEntry->State = resourceStatus.ResourceState; DummyReleaseResourceLock( ResourceEntry ); return status; } //*** DummyOfflineThread() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyTerminate // // Routine Description: // // Terminate routine for Dummy resources. // // Take the specified resource offline immediately (the resource is // unavailable for use). // // Arguments: // // ResourceId - Supplies the resource id for the resource to be brought // offline. // // Return Value: // // None. // //-- ///////////////////////////////////////////////////////////////////////////// void WINAPI DummyTerminate( IN RESID ResourceId ) { PDUMMY_RESOURCE ResourceEntry; ResourceEntry = (PDUMMY_RESOURCE)ResourceId; if ( ResourceEntry == NULL ) { DBG_PRINT( "Dummy: Terminate request for a nonexistent resource id 0x%p\n", ResourceId ); return; } if ( ResourceEntry->ResId != ResourceId ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Terminate resource sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return; } DummyAcquireResourceLock( ResourceEntry ); #ifdef LOG_VERBOSE (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Terminate request.\n" ); #endif // // Terminate the resource. // DummyDoTerminate( ResourceEntry ); ResourceEntry->State = ClusterResourceOffline; DummyReleaseResourceLock( ResourceEntry ); return; } //*** DummyTerminate() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyDoTerminate // // Routine Description: // // Do the actual Terminate work for Dummy resources. // // Arguments: // // ResourceEntry - Supplies resource entry for resource to be terminated // // Return Value: // // ERROR_SUCCESS - The request completed successfully and the resource is // offline. // // Win32 error code - Will cause the resource monitor to log an event and // call the Terminate routine. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD DummyDoTerminate( IN PDUMMY_RESOURCE ResourceEntry ) { DWORD status = ERROR_SUCCESS; if ( ResourceEntry->TimerType != TimerNotUsed ) { SetEvent( ResourceEntry->TimerThreadWakeup ); } // // Kill off any pending threads. // ClusWorkerTerminate( &ResourceEntry->OnlineThread ); ClusWorkerTerminate( &ResourceEntry->OfflineThread ); // // Terminate the resource. // // TODO: Add code to terminate your resource. if ( status == ERROR_SUCCESS ) { ResourceEntry->State = ClusterResourceOffline; } return status; } //*** DummyDoTerminate() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyLooksAlive // // Routine Description: // // LooksAlive routine for Dummy resources. // // Perform a quick check to determine if the specified resource is probably // online (available for use). This call should not block for more than // 300 ms, preferably less than 50 ms. // // Arguments: // // ResourceId - Supplies the resource id for the resource to polled. // // Return Value: // // TRUE - The specified resource is probably online and available for use. // // FALSE - The specified resource is not functioning normally. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL WINAPI DummyLooksAlive( IN RESID ResourceId ) { PDUMMY_RESOURCE ResourceEntry; ResourceEntry = (PDUMMY_RESOURCE)ResourceId; if ( ResourceEntry == NULL ) { DBG_PRINT("Dummy: LooksAlive request for a nonexistent resource id 0x%p\n", ResourceId ); return FALSE; } if ( ResourceEntry->ResId != ResourceId ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"LooksAlive sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return FALSE; } #ifdef LOG_VERBOSE (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"LooksAlive request.\n" ); #endif // TODO: LooksAlive code // NOTE: LooksAlive should be a quick check to see if the resource is // available or not, whereas IsAlive should be a thorough check. If // there are no differences between a quick check and a thorough check, // IsAlive can be called for LooksAlive, as it is below. However, if there // are differences, replace the call to IsAlive below with your quick // check code. // // Check to see if the resource is alive. // return DummyCheckIsAlive( ResourceEntry ); } //*** DummyLooksAlive() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyIsAlive // // Routine Description: // // IsAlive routine for Dummy resources. // // Perform a thorough check to determine if the specified resource is online // (available for use). This call should not block for more than 400 ms, // preferably less than 100 ms. // // Arguments: // // ResourceId - Supplies the resource id for the resource to polled. // // Return Value: // // TRUE - The specified resource is online and functioning normally. // // FALSE - The specified resource is not functioning normally. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL WINAPI DummyIsAlive( IN RESID ResourceId ) { PDUMMY_RESOURCE ResourceEntry; ResourceEntry = (PDUMMY_RESOURCE)ResourceId; if ( ResourceEntry == NULL ) { DBG_PRINT("Dummy: IsAlive request for a nonexistent resource id 0x%p\n", ResourceId ); return FALSE; } if ( ResourceEntry->ResId != ResourceId ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"IsAlive sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return FALSE; } #ifdef LOG_VERBOSE (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"IsAlive request.\n" ); #endif // // Check to see if the resource is alive. // return DummyCheckIsAlive( ResourceEntry ); } //*** DummyIsAlive() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyCheckIsAlive // // Routine Description: // // Check to see if the resource is alive for Dummy resources. // // Arguments: // // ResourceEntry - Supplies the resource entry for the resource to polled. // // Return Value: // // TRUE - The specified resource is online and functioning normally. // // FALSE - The specified resource is not functioning normally. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL DummyCheckIsAlive( IN PDUMMY_RESOURCE ResourceEntry ) { DummyAcquireResourceLock( ResourceEntry ); // // Check to see if the resource is alive. // // TODO: Add code to determine if your resource is alive. DummyReleaseResourceLock( ResourceEntry ); return TRUE; } //*** DummyCheckIsAlive() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyResourceControl // // Routine Description: // // ResourceControl routine for Dummy resources. // // Perform the control request specified by ControlCode on the specified // resource. // // Arguments: // // ResourceId - Supplies the resource id for the specific resource. // // ControlCode - Supplies the control code that defines the action // to be performed. // // InBuffer - Supplies a pointer to a buffer containing input data. // // InBufferSize - Supplies the size, in bytes, of the data pointed // to by InBuffer. // // OutBuffer - Supplies a pointer to the output buffer to be filled in. // // OutBufferSize - Supplies the size, in bytes, of the available space // pointed to by OutBuffer. // // BytesReturned - Returns the number of bytes of OutBuffer actually // filled in by the resource. If OutBuffer is too small, BytesReturned // contains the total number of bytes for the operation to succeed. // // Return Value: // // ERROR_SUCCESS - The function completed successfully. // // ERROR_RESOURCE_NOT_FOUND - RESID is not valid. // // ERROR_INVALID_FUNCTION - The requested control code is not supported. // In some cases, this allows the cluster software to perform the work. // // Win32 error code - The function failed. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD WINAPI DummyResourceControl( IN RESID ResourceId, IN DWORD ControlCode, IN void * InBuffer, IN DWORD InBufferSize, OUT void * OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) { DWORD status; PDUMMY_RESOURCE ResourceEntry; DWORD required; ResourceEntry = (PDUMMY_RESOURCE)ResourceId; if ( ResourceEntry == NULL ) { DBG_PRINT("Dummy: ResourceControl request for a nonexistent resource id 0x%p\n", ResourceId ); return ERROR_RESOURCE_NOT_FOUND; } if ( ResourceEntry->ResId != ResourceId ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"ResourceControl sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return ERROR_RESOURCE_NOT_FOUND; } DummyAcquireResourceLock( ResourceEntry ); switch ( ControlCode ) { case CLUSCTL_RESOURCE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( DummyResourcePrivateProperties, (LPWSTR)OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES: status = DummyGetPrivateResProperties( ResourceEntry, OutBuffer, OutBufferSize, BytesReturned ); break; case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES: status = DummyValidatePrivateResProperties( ResourceEntry, InBuffer, InBufferSize, NULL ); break; case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES: status = DummySetPrivateResProperties( ResourceEntry, InBuffer, InBufferSize ); break; default: status = ERROR_INVALID_FUNCTION; break; } DummyReleaseResourceLock( ResourceEntry ); return status; } //*** DummyResourceControl() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyResourceTypeControl // // Routine Description: // // ResourceTypeControl routine for Dummy resources. // // Perform the control request specified by ControlCode. // // Arguments: // // ResourceTypeName - Supplies the name of the resource type. // // ControlCode - Supplies the control code that defines the action // to be performed. // // InBuffer - Supplies a pointer to a buffer containing input data. // // InBufferSize - Supplies the size, in bytes, of the data pointed // to by InBuffer. // // OutBuffer - Supplies a pointer to the output buffer to be filled in. // // OutBufferSize - Supplies the size, in bytes, of the available space // pointed to by OutBuffer. // // BytesReturned - Returns the number of bytes of OutBuffer actually // filled in by the resource. If OutBuffer is too small, BytesReturned // contains the total number of bytes for the operation to succeed. // // Return Value: // // ERROR_SUCCESS - The function completed successfully. // // ERROR_INVALID_FUNCTION - The requested control code is not supported. // In some cases, this allows the cluster software to perform the work. // // Win32 error code - The function failed. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD WINAPI DummyResourceTypeControl( IN LPCWSTR, //ResourceTypeName, IN DWORD ControlCode, IN void *, //InBuffer, IN DWORD, //InBufferSize, OUT void * OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) { DWORD status; DWORD required; switch ( ControlCode ) { case CLUSCTL_RESOURCE_TYPE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( DummyResourcePrivateProperties, (LPWSTR)OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; default: status = ERROR_INVALID_FUNCTION; break; } return status; } //*** DummyResourceTypeControl() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyGetPrivateResProperties // // Routine Description: // // Processes the CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES control function // for resources of type Dummy. // // Arguments: // // ResourceEntry - Supplies the resource entry on which to operate. // // OutBuffer - Returns the output data. // // OutBufferSize - Supplies the size, in bytes, of the data pointed // to by OutBuffer. // // BytesReturned - The number of bytes returned in OutBuffer. // // Return Value: // // ERROR_SUCCESS - The function completed successfully. // // ERROR_INVALID_PARAMETER - The data is formatted incorrectly. // // ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory. // // Win32 error code - The function failed. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD DummyGetPrivateResProperties( IN OUT PDUMMY_RESOURCE ResourceEntry, OUT void * OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) { DWORD status; DWORD required; DummyAcquireResourceLock( ResourceEntry ); status = ResUtilGetAllProperties( ResourceEntry->ParametersKey, DummyResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } DummyReleaseResourceLock( ResourceEntry ); return status; } //*** DummyGetPrivateResProperties() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyValidatePrivateResProperties // // Routine Description: // // Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control // function for resources of type Dummy. // // Arguments: // // pResourceEntry - Supplies the resource entry on which to operate. // // InBuffer - Supplies a pointer to a buffer containing input data. // // InBufferSize - Supplies the size, in bytes, of the data pointed // to by InBuffer. // // Params - Supplies the parameter block to fill in. // // Return Value: // // ERROR_SUCCESS - The function completed successfully. // // ERROR_INVALID_PARAMETER - The data is formatted incorrectly. // // ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory. // // Win32 error code - The function failed. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD DummyValidatePrivateResProperties( IN OUT PDUMMY_RESOURCE pResourceEntry, IN const PVOID InBuffer, IN DWORD InBufferSize, OUT PDUMMY_PARAMS Params ) { DWORD status = ERROR_SUCCESS; DUMMY_PARAMS propsCurrent; DUMMY_PARAMS propsNew; PDUMMY_PARAMS pParams; LPWSTR pszNameOfPropInError; DummyAcquireResourceLock( pResourceEntry ); // // Check if there is input data. // if ( ( InBuffer == NULL ) || ( InBufferSize < sizeof( DWORD ) ) ) { status = ERROR_INVALID_DATA; goto exit; } // // Retrieve the current set of private properties from the // cluster database. // ZeroMemory( &propsCurrent, sizeof( propsCurrent ) ); status = ResUtilGetPropertiesToParameterBlock( pResourceEntry->ParametersKey, DummyResourcePrivateProperties, reinterpret_cast< LPBYTE >( &propsCurrent ), FALSE, /*CheckForRequiredProperties*/ &pszNameOfPropInError ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( pResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to read the '%1' property. Error: %2!u!.\n", (pszNameOfPropInError == NULL ? L"" : pszNameOfPropInError), status ); goto exit; } // if: error getting properties // // Duplicate the resource parameter block. // if ( Params == NULL ) { pParams = &propsNew; } else { pParams = Params; } ZeroMemory( pParams, sizeof(DUMMY_PARAMS) ); status = ResUtilDupParameterBlock( reinterpret_cast< LPBYTE >( pParams ), reinterpret_cast< LPBYTE >( &propsCurrent ), DummyResourcePrivateProperties ); if ( status != ERROR_SUCCESS ) { goto cleanup; } // // Parse and validate the properties. // status = ResUtilVerifyPropertyTable( DummyResourcePrivateProperties, NULL, TRUE, // AllowUnknownProperties InBuffer, InBufferSize, (LPBYTE)pParams ); if ( status == ERROR_SUCCESS ) { // // Validate the parameter values. // // TODO: Code to validate interactions between parameters goes here. } cleanup: // // Cleanup our parameter block. // if ( (pParams == &propsNew) || ( (status != ERROR_SUCCESS) && (pParams != NULL) ) ) { ResUtilFreeParameterBlock( reinterpret_cast< LPBYTE >( pParams ), reinterpret_cast< LPBYTE >( &propsCurrent ), DummyResourcePrivateProperties ); } // if: we duplicated the parameter block ResUtilFreeParameterBlock( reinterpret_cast< LPBYTE >( &propsCurrent ), NULL, DummyResourcePrivateProperties ); exit: DummyReleaseResourceLock( pResourceEntry ); return status; } //*** DummyValidatePrivateResProperties() ///////////////////////////////////////////////////////////////////////////// //++ // // DummySetPrivateResProperties // // Routine Description: // // Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function // for resources of type Dummy. // // Arguments: // // ResourceEntry - Supplies the resource entry on which to operate. // // InBuffer - Supplies a pointer to a buffer containing input data. // // InBufferSize - Supplies the size, in bytes, of the data pointed // to by InBuffer. // // Return Value: // // ERROR_SUCCESS - The function completed successfully. // // ERROR_INVALID_PARAMETER - The data is formatted incorrectly. // // ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory. // // Win32 error code - The function failed. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD DummySetPrivateResProperties( IN OUT PDUMMY_RESOURCE ResourceEntry, IN void * InBuffer, IN DWORD InBufferSize ) { DWORD status = ERROR_SUCCESS; DUMMY_PARAMS params; DummyAcquireResourceLock( ResourceEntry ); // // Parse the properties so they can be validated together. // This routine does individual property validation. // status = DummyValidatePrivateResProperties( ResourceEntry, InBuffer, InBufferSize, ¶ms ); if ( status != ERROR_SUCCESS ) { ResUtilFreeParameterBlock( (LPBYTE)¶ms, (LPBYTE)&ResourceEntry->Params, DummyResourcePrivateProperties ); goto exit; } // // Save the parameter values. // status = ResUtilSetPropertyParameterBlock( ResourceEntry->ParametersKey, DummyResourcePrivateProperties, NULL, (LPBYTE) ¶ms, InBuffer, InBufferSize, (LPBYTE) &ResourceEntry->Params ); ResUtilFreeParameterBlock( (LPBYTE)¶ms, (LPBYTE)&ResourceEntry->Params, DummyResourcePrivateProperties ); // // If the resource is online, return a non-success status. // // TODO: Modify the code below if your resource can handle // changes to properties while it is still online. if ( status == ERROR_SUCCESS ) { if ( ResourceEntry->State == ClusterResourceOnline ) { status = ERROR_RESOURCE_PROPERTIES_STORED; } else if ( ResourceEntry->State == ClusterResourceOnlinePending ) { status = ERROR_RESOURCE_PROPERTIES_STORED; } else { status = ERROR_SUCCESS; } } exit: DummyReleaseResourceLock( ResourceEntry ); return status; } //*** DummySetPrivateResProperties() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyDoPending // // Routine Description: // // Does the online and offline pending and waiting processing // // Arguments: // // resource - A pointer to the DummyResource block for this resource. // // nDelay - How long should we wait? // // WorkerPtr - Supplies the worker structure // // Return Value: // // ERROR_SUCCESS if successful. // // Win32 error code on failure. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD DummyDoPending( IN PDUMMY_RESOURCE ResourceEntry, IN DWORD nDelay, IN PCLUS_WORKER WorkerPtr ) { RESOURCE_STATUS resourceStatus; DWORD status; DWORD nWait = MAX_WAIT; RESOURCE_EXIT_STATE exit; ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ( ResourceEntry->TimerType == TimerOnlinePending ? ClusterResourceOnlinePending : ClusterResourceOfflinePending ); resourceStatus.WaitHint = 0; resourceStatus.CheckPoint = 1; (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); if ( nDelay < nWait ) { nWait = nDelay; nDelay = 0; } for ( ; ; ) { status = WaitForSingleObject( ResourceEntry->TimerThreadWakeup, nWait ); // // Check to see if the online operation was aborted while this thread // was starting up. // if ( ClusWorkerCheckTerminate( WorkerPtr ) ) { status = ERROR_OPERATION_ABORTED; ResourceEntry->State = ( ResourceEntry->TimerType == TimerOnlinePending ) ? ClusterResourceOnlinePending : ClusterResourceOfflinePending; break; } // // Either the terminate routine was called, or we timed out. // If we timed out, then indicate that we've completed. // if ( status == WAIT_TIMEOUT ) { if ( nDelay == 0 ) { status = ERROR_SUCCESS; break; } nDelay -= nWait; if ( nDelay < nWait ) { nWait = nDelay; nDelay = 0; } } else { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, ( ResourceEntry->TimerType == TimerOnlinePending ) ? L"Online pending terminated\n" : L"Offline pending terminated\n" ); if ( ResourceEntry->State == ClusterResourceOffline ) { ResourceEntry->TimerType = TimerOfflinePending; break; } else if ( ResourceEntry->State == ClusterResourceOnline ) { ResourceEntry->TimerType = TimerOnlinePending; break; } } exit = (_RESOURCE_EXIT_STATE)(g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); if ( exit == ResourceExitStateTerminate ) { ResourceEntry->State = ( ResourceEntry->TimerType == TimerOnlinePending ) ? ClusterResourceOnline : ClusterResourceOffline; status = ERROR_SUCCESS; //TODO if ( ResourceEntry->TimerType == TimerOnlinePending ) { break; } } else { ResourceEntry->State = ( ResourceEntry->TimerType == TimerOnlinePending ) ? ClusterResourceOnline : ClusterResourceOffline; status = ERROR_SUCCESS; } } // for: resourceStatus.ResourceState = ( ResourceEntry->TimerType == TimerOnlinePending ? ClusterResourceOnline : ClusterResourceOffline ); (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); return status; } //*** DummyDoPending() ///////////////////////////////////////////////////////////////////////////// //++ // // DummyTimerThread // // Routine Description: // // Starts a timer thread to wait and signal failures // // Arguments: // // resource - A pointer to the DummyResource block for this resource. // // WorkerPtr - Supplies the worker structure // // Return Value: // // ERROR_SUCCESS if successful. // // Win32 error code on failure. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD DummyTimerThread( IN PDUMMY_RESOURCE ResourceEntry, IN PCLUS_WORKER WorkerPtr ) { RESOURCE_STATUS resourceStatus; SYSTEMTIME time; DWORD delay; DWORD status = ERROR_SUCCESS; DummyAcquireResourceLock( ResourceEntry ); (g_LogEvent)( NULL, LOG_INFORMATION, L"TimerThread Entry\n" ); // // If we are not running in async failure mode, or // pending mode then exit now. // if ( !AsyncMode( ResourceEntry ) && !PendingMode( ResourceEntry ) ) { status = ERROR_SUCCESS; goto exit; } // // Check to see if the online/offline operation was aborted while this thread // was starting up. // if ( ClusWorkerCheckTerminate( WorkerPtr ) ) { status = ERROR_OPERATION_ABORTED; ResourceEntry->State = ClusterResourceOfflinePending; goto exit; } more_pending: ResUtilInitializeResourceStatus( &resourceStatus ); // // Otherwise, get system time for random delay. // if ( ResourceEntry->Params.PendTime == 0 ) { GetSystemTime( &time ); delay = ( time.wMilliseconds + time.wSecond ) * 6; } else { delay = ResourceEntry->Params.PendTime * 1000; } // // Use longer delays for errors // if ( ResourceEntry->TimerType == TimerErrorPending ) { delay *= 10; } // // This routine is either handling an Offline Pending or an error timeout. // switch ( ResourceEntry->TimerType ) { case TimerOnlinePending : { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Will complete online in approximately %1!u! seconds\n", ( delay + 500 ) / 1000 ); status = DummyDoPending( ResourceEntry, delay, WorkerPtr ); break; } case TimerOfflinePending : { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Will complete offline in approximately %1!u! seconds\n", (delay+500)/1000 ); status = DummyDoPending( ResourceEntry, delay, WorkerPtr ); break; } case TimerErrorPending : { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Will fail in approximately %1!u! seconds\n", ( delay + 500 ) / 1000 ); if ( !ResetEvent( ResourceEntry->SignalEvent ) ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to reset the signal event\n"); status = ERROR_GEN_FAILURE; goto exit; } status = WaitForSingleObject( ResourceEntry->TimerThreadWakeup, delay ); // // Either the terminate routine was called, or we timed out. // If we timed out, then signal the waiting event. // if ( status == WAIT_TIMEOUT ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Failed randomly\n"); ResourceEntry->TimerType = TimerNotUsed; SetEvent( ResourceEntry->SignalEvent ); } else { if ( ResourceEntry->State == ClusterResourceOfflinePending ) { ResourceEntry->TimerType = TimerOfflinePending; goto more_pending; } } break; } default: (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"DummyTimer internal error, timer %1!u!\n", ResourceEntry->TimerType); break; } (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"TimerThread Exit\n" ); ResourceEntry->TimerType = TimerNotUsed; exit: DummyReleaseResourceLock( ResourceEntry ); return status; } // DummyTimerThread //*********************************************************** // // Define Function Table // //*********************************************************** CLRES_V1_FUNCTION_TABLE( g_DummyFunctionTable, // Name CLRES_VERSION_V1_00, // Version Dummy, // Prefix NULL, // Arbitrate NULL, // Release DummyResourceControl, // ResControl DummyResourceTypeControl // ResTypeControl );