|
|
/////////////////////////////////////////////////////////////////////////////
//
// 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 <windows.h>
#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 <clusapi.h>
#include <resapi.h>
#include <stdio.h>
//
// 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
);
|