You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2280 lines
56 KiB
2280 lines
56 KiB
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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
|
|
);
|