windows-nt/Source/XPSP1/NT/base/cluster/resdll/dummy/dummy.cpp
2020-09-26 16:20:57 +08:00

2281 lines
54 KiB
C++

/////////////////////////////////////////////////////////////////////////////
//
// 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, &parametersKey );
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, &params );
if ( status != ERROR_SUCCESS )
{
ResUtilFreeParameterBlock( (LPBYTE)&params, (LPBYTE)&ResourceEntry->Params, DummyResourcePrivateProperties );
goto exit;
}
//
// Save the parameter values.
//
status = ResUtilSetPropertyParameterBlock(
ResourceEntry->ParametersKey,
DummyResourcePrivateProperties,
NULL,
(LPBYTE) &params,
InBuffer,
InBufferSize,
(LPBYTE) &ResourceEntry->Params
);
ResUtilFreeParameterBlock( (LPBYTE)&params, (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
);