mirror of https://github.com/tongzx/nt5src
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.
1527 lines
36 KiB
1527 lines
36 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dummyiis.c
|
|
|
|
Abstract:
|
|
|
|
This file was adapted from \nt\private\cluster\resdll\dummy\dummy.c. It
|
|
implements an NT Cluster resource DLL for the IIS Virtual Root resource type.
|
|
This resource does nothing except read the value "Parameters\Failed" from the
|
|
resource definition in the registry on each poll to determine whether or not the resource
|
|
has failed.
|
|
|
|
The DLL is to be installed during UPGRADE from NT 4.0. It will become obsolete
|
|
with the availability of a IIS Virtual Root resource DLL that works with IIS 5.
|
|
|
|
Author:
|
|
|
|
John Vert (jvert) 5-Dec-1995
|
|
|
|
Revision History:
|
|
|
|
C. Brent Thomas (a-brentt) 29-May-1998
|
|
|
|
Adapted for IIS Virtual Root reaource type.
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include "clusapi.h"
|
|
#include "resapi.h"
|
|
|
|
//
|
|
// Local Types.
|
|
//
|
|
|
|
typedef enum TimerType {
|
|
TimerNotUsed,
|
|
TimerErrorPending,
|
|
TimerOnlinePending,
|
|
TimerOfflinePending
|
|
};
|
|
|
|
typedef struct {
|
|
DWORD Flags;
|
|
CLUSTER_RESOURCE_STATE State;
|
|
HANDLE SignalEvent;
|
|
HANDLE TimerThreadWakeup;
|
|
HANDLE ThreadHandle;
|
|
HKEY ParametersKey;
|
|
RESID ResourceId;
|
|
DWORD TimerType;
|
|
RESOURCE_HANDLE ResourceHandle;
|
|
DWORD PendTime;
|
|
} DUMMY_RESOURCE, *PDUMMY_RESOURCE;
|
|
// 10 dwords ( 0x28 offset )
|
|
|
|
//
|
|
// Constants
|
|
//
|
|
#define DUMMY_RESOURCE_BLOCK_SIZE 4
|
|
|
|
#define DUMMY_FLAG_VALID 0x00000001
|
|
#define DUMMY_FLAG_ASYNC 0x00000002 // Asynchronous failure mode
|
|
#define DUMMY_FLAG_PENDING 0x00000004 // Pending mode on shutdown
|
|
|
|
#define DUMMY_PARAMETER_FAILED L"Failed"
|
|
#define DUMMY_PARAMETER_ASYNC L"Asynchronous"
|
|
#define DUMMY_PARAMETER_PENDING L"Pending"
|
|
#define DUMMY_PARAMETER_OPENSFAIL L"OpensFail"
|
|
#define DUMMY_PARAMETER_PENDTIME L"PendTime"
|
|
|
|
#define DUMMY_PRINT printf
|
|
|
|
//
|
|
// Macros
|
|
//
|
|
#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() \
|
|
{ \
|
|
DWORD status; \
|
|
status = WaitForSingleObject(DummyResourceSemaphore, INFINITE); \
|
|
}
|
|
|
|
#define DummyReleaseResourceLock() \
|
|
{ \
|
|
ReleaseSemaphore(DummyResourceSemaphore, 1, NULL); \
|
|
}
|
|
|
|
|
|
//
|
|
// Local Variables
|
|
//
|
|
PDUMMY_RESOURCE DummyResourceList = NULL;
|
|
DWORD DummyResourceListSize = DUMMY_RESOURCE_BLOCK_SIZE;
|
|
HANDLE DummyResourceSemaphore = NULL;
|
|
PLOG_EVENT_ROUTINE LogEvent;
|
|
PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus;
|
|
|
|
extern CLRES_FUNCTION_TABLE DumbFunctionTable;
|
|
|
|
|
|
//
|
|
// Local utility functions
|
|
//
|
|
|
|
|
|
DWORD
|
|
DummyInit(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates and initializes global system resources for the Dummy resource.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
A Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
|
|
|
|
DummyResourceSemaphore = CreateSemaphore(NULL, 0, 1 , NULL);
|
|
|
|
if (DummyResourceSemaphore == NULL) {
|
|
status = GetLastError();
|
|
DUMMY_PRINT(
|
|
"DUMMY: unable to create resource mutex, error %u\n",
|
|
status
|
|
);
|
|
return(status);
|
|
}
|
|
|
|
if ( GetLastError() != ERROR_ALREADY_EXISTS ) {
|
|
ReleaseSemaphore( DummyResourceSemaphore, 1, NULL );
|
|
}
|
|
|
|
DummyResourceList = LocalAlloc(
|
|
LMEM_FIXED,
|
|
DummyResourceListSize * sizeof(DUMMY_RESOURCE)
|
|
);
|
|
|
|
if (DummyResourceList == NULL) {
|
|
DUMMY_PRINT("DUMMY: unable to allocate resource list\n");
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
CloseHandle(DummyResourceSemaphore);
|
|
DummyResourceSemaphore = NULL;
|
|
return(status);
|
|
}
|
|
|
|
ZeroMemory(
|
|
DummyResourceList,
|
|
DummyResourceListSize * sizeof(DUMMY_RESOURCE)
|
|
);
|
|
|
|
return(NO_ERROR);
|
|
|
|
} // DummyInit
|
|
|
|
|
|
|
|
VOID
|
|
DummyCleanup(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deallocates any system resources in use by the Dummy resource.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (DummyResourceList != NULL) {
|
|
LocalFree(DummyResourceList);
|
|
DummyResourceList = NULL;
|
|
}
|
|
|
|
if (DummyResourceSemaphore != NULL) {
|
|
CloseHandle(DummyResourceSemaphore);
|
|
DummyResourceSemaphore = NULL;
|
|
}
|
|
|
|
return;
|
|
|
|
} // DummyCleanup
|
|
|
|
|
|
|
|
PDUMMY_RESOURCE
|
|
DummyAllocateResource(
|
|
OUT RESID *ResourceId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a resource structure from the free pool.
|
|
|
|
Arguments:
|
|
|
|
ResourceId - Filled in with the RESID for the allocated resource
|
|
on return.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the allocated resource, or NULL on error.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDUMMY_RESOURCE resource = NULL;
|
|
PDUMMY_RESOURCE newList;
|
|
DWORD i;
|
|
DWORD newSize = DummyResourceListSize +
|
|
DUMMY_RESOURCE_BLOCK_SIZE;
|
|
|
|
for (i=1; i < DummyResourceListSize; i++) {
|
|
if (!(DummyResourceList[i].Flags & DUMMY_FLAG_VALID)) {
|
|
resource = DummyResourceList + i;
|
|
*ResourceId = (RESID)ULongToPtr( i );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (resource == NULL) {
|
|
newList = LocalReAlloc(
|
|
DummyResourceList,
|
|
newSize * sizeof(DUMMY_RESOURCE),
|
|
LMEM_MOVEABLE
|
|
);
|
|
|
|
if (newList == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
DummyResourceList = newList;
|
|
resource = DummyResourceList + DummyResourceListSize;
|
|
*ResourceId = (RESID) ULongToPtr( DummyResourceListSize );
|
|
DummyResourceListSize = newSize;
|
|
ZeroMemory(resource, DUMMY_RESOURCE_BLOCK_SIZE * sizeof(DUMMY_RESOURCE));
|
|
}
|
|
|
|
ZeroMemory(resource, sizeof(DUMMY_RESOURCE));
|
|
|
|
resource->Flags = DUMMY_FLAG_VALID;
|
|
|
|
return(resource);
|
|
|
|
} // DummyAllocateResource
|
|
|
|
|
|
|
|
VOID
|
|
DummyFreeResource(
|
|
IN PDUMMY_RESOURCE Resource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a resource structure to the free pool.
|
|
|
|
Arguments:
|
|
|
|
Resource - A pointer to the resource structure to free.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
Resource->Flags = 0;
|
|
|
|
return;
|
|
|
|
} // DummyFreeResource
|
|
|
|
|
|
|
|
PDUMMY_RESOURCE
|
|
DummyFindResource(
|
|
IN RESID ResourceId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Locates the Dummy resource structure identified by a RESID.
|
|
|
|
Arguments:
|
|
|
|
ResourceId - The RESID of the resource to locate.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the resource strucuture, or NULL on error.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD index = PtrToUlong(ResourceId);
|
|
PDUMMY_RESOURCE resource = NULL;
|
|
|
|
|
|
if (index < DummyResourceListSize) {
|
|
|
|
if (DummyResourceList[index].Flags & DUMMY_FLAG_VALID) {
|
|
resource = DummyResourceList + index;
|
|
}
|
|
}
|
|
|
|
return(resource);
|
|
|
|
} // DummyFindResource
|
|
|
|
|
|
|
|
DWORD
|
|
DummyTimerThread(
|
|
IN LPVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Starts a timer thread to wait and signal failures
|
|
|
|
Arguments:
|
|
|
|
Context - A pointer to the DummyResource block for this resource.
|
|
|
|
Returns:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDUMMY_RESOURCE resource = (PDUMMY_RESOURCE)Context;
|
|
RESOURCE_STATUS resourceStatus;
|
|
SYSTEMTIME time;
|
|
DWORD delay;
|
|
DWORD status;
|
|
|
|
(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(resource) && !PendingMode(resource) ) {
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
more_pending:
|
|
|
|
ResUtilInitializeResourceStatus( &resourceStatus );
|
|
|
|
//
|
|
// Otherwise, get system time for random delay.
|
|
//
|
|
if (resource->PendTime == 0) {
|
|
GetSystemTime(&time);
|
|
|
|
delay = (time.wMilliseconds + time.wSecond) * 6;
|
|
} else {
|
|
delay = resource->PendTime * 1000;
|
|
}
|
|
|
|
|
|
// Use longer delays for errors
|
|
if ( resource->TimerType == TimerErrorPending ) {
|
|
delay = delay*10;
|
|
}
|
|
|
|
//
|
|
// This routine is either handling an Offline Pending or an error timeout.
|
|
//
|
|
|
|
switch ( resource->TimerType ) {
|
|
|
|
case TimerOnlinePending:
|
|
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Will complete online in approximately %1!u! seconds\n",
|
|
(delay+500)/1000);
|
|
|
|
resourceStatus.ResourceState = ClusterResourceOnline;
|
|
resourceStatus.WaitHint = 0;
|
|
resourceStatus.CheckPoint = 1;
|
|
|
|
status = WaitForSingleObject( resource->TimerThreadWakeup, delay );
|
|
|
|
//
|
|
// Either the terminate routine was called, or we timed out.
|
|
// If we timed out, then indicate that we've completed.
|
|
//
|
|
|
|
if ( status == WAIT_TIMEOUT ) {
|
|
GetSystemTime(&time);
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online resource state transition complete at %1!02d!:%2!02d!:%3!02d!.%4!03d!\n",
|
|
time.wHour,
|
|
time.wMinute,
|
|
time.wSecond,
|
|
time.wMilliseconds);
|
|
|
|
resource->State = ClusterResourceOnline;
|
|
(SetResourceStatus)(resource->ResourceHandle,
|
|
&resourceStatus);
|
|
if ( AsyncMode( resource ) ) {
|
|
resource->TimerType = TimerErrorPending;
|
|
goto more_pending;
|
|
}
|
|
} else {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online pending terminated\n");
|
|
if ( resource->State == ClusterResourceOffline ) {
|
|
resource->TimerType = TimerOfflinePending;
|
|
goto more_pending;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TimerOfflinePending:
|
|
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Will complete offline in approximately %1!u! seconds\n",
|
|
(delay+500)/1000);
|
|
|
|
resourceStatus.ResourceState = ClusterResourceOffline;
|
|
resourceStatus.WaitHint = 0;
|
|
resourceStatus.CheckPoint = 1;
|
|
|
|
status = WaitForSingleObject( resource->TimerThreadWakeup, delay );
|
|
|
|
//
|
|
// Either the terminate routine was called, or we timed out.
|
|
// If we timed out, then indicate that we've completed.
|
|
//
|
|
|
|
if ( status == WAIT_TIMEOUT ) {
|
|
GetSystemTime(&time);
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Offline resource state transition complete at %1!02d!:%2!02d!:%3!02d!.%4!03d!\n",
|
|
time.wHour,
|
|
time.wMinute,
|
|
time.wSecond,
|
|
time.wMilliseconds);
|
|
|
|
resource->State = ClusterResourceOffline;
|
|
(SetResourceStatus)(resource->ResourceHandle,
|
|
&resourceStatus);
|
|
} else {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Offline pending terminated\n");
|
|
}
|
|
break;
|
|
|
|
case TimerErrorPending:
|
|
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Will fail in approximately %1!u! seconds\n",
|
|
(delay+500)/1000);
|
|
|
|
if ( !ResetEvent( resource->SignalEvent ) ) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to reset the signal event\n");
|
|
resource->ThreadHandle = NULL;
|
|
return(ERROR_GEN_FAILURE);
|
|
}
|
|
|
|
status = WaitForSingleObject( resource->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 ) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Failed randomly\n");
|
|
resource->TimerType = TimerNotUsed;
|
|
SetEvent( resource->SignalEvent );
|
|
} else {
|
|
if ( resource->State == ClusterResourceOfflinePending ) {
|
|
resource->TimerType = TimerOfflinePending;
|
|
goto more_pending;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"DummyTimer internal error, timer %1!u!\n",
|
|
resource->TimerType);
|
|
break;
|
|
|
|
}
|
|
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"TimerThread Exit\n");
|
|
|
|
|
|
resource->TimerType = TimerNotUsed;
|
|
resource->ThreadHandle = NULL;
|
|
return(ERROR_SUCCESS);
|
|
|
|
} // DummyTimerThread
|
|
|
|
|
|
|
|
//
|
|
// Public Functions
|
|
//
|
|
|
|
BOOL
|
|
WINAPI
|
|
DummyDllEntryPoint(
|
|
IN HINSTANCE DllHandle,
|
|
IN DWORD Reason,
|
|
IN LPVOID Reserved
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialization & cleanup routine for Dummy resource DLL.
|
|
|
|
Arguments:
|
|
|
|
DllHandle - Handle to the DLL.
|
|
|
|
Reason - The reason this routine is being invoked.
|
|
|
|
Return Value:
|
|
|
|
On PROCESS_ATTACH: TRUE if the DLL initialized successfully
|
|
FALSE if it did not.
|
|
|
|
The return value from all other calls is ignored.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
|
|
|
|
switch(Reason) {
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
status = DummyInit();
|
|
|
|
if (status != NO_ERROR) {
|
|
SetLastError(status);
|
|
return(FALSE);
|
|
}
|
|
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
DummyCleanup();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
} // DummyDllEntryPoint
|
|
|
|
|
|
|
|
//
|
|
// Define the resource Name
|
|
//
|
|
#define IIS_RESOURCE_NAME L"IIS Virtual Root"
|
|
|
|
DWORD
|
|
WINAPI
|
|
Startup(
|
|
IN LPCWSTR ResourceType,
|
|
IN DWORD MinVersionSupported,
|
|
IN DWORD MaxVersionSupported,
|
|
IN PSET_RESOURCE_STATUS_ROUTINE pSetResourceStatus,
|
|
IN PLOG_EVENT_ROUTINE pLogEvent,
|
|
OUT PCLRES_FUNCTION_TABLE *FunctionTable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Startup a particular resource type. This means verifying the version
|
|
requested, and returning the function table for this resource type.
|
|
|
|
Arguments:
|
|
|
|
ResourceType - Supplies the type of resource.
|
|
|
|
MinVersionSupported - The minimum version number supported by the cluster
|
|
service on this system.
|
|
|
|
MaxVersionSupported - The maximum version number supported by the cluster
|
|
service on this system.
|
|
|
|
FunctionTable - Returns the Function Table for this resource type.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
if ( lstrcmpiW( ResourceType, IIS_RESOURCE_NAME ) != 0 )
|
|
{
|
|
return (ERROR_UNKNOWN_REVISION);
|
|
}
|
|
|
|
LogEvent = pLogEvent;
|
|
SetResourceStatus = pSetResourceStatus;
|
|
|
|
if ( (MinVersionSupported <= CLRES_VERSION_V1_00) &&
|
|
(MaxVersionSupported >= CLRES_VERSION_V1_00) )
|
|
{
|
|
*FunctionTable = &DumbFunctionTable;
|
|
return (ERROR_SUCCESS);
|
|
}
|
|
|
|
return (ERROR_REVISION_MISMATCH);
|
|
|
|
} // Startup
|
|
|
|
|
|
|
|
RESID
|
|
WINAPI
|
|
DumbOpen(
|
|
IN LPCWSTR ResourceName,
|
|
IN HKEY ResourceKey,
|
|
IN RESOURCE_HANDLE ResourceHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open (initialize) routine for Dummy resource
|
|
|
|
Arguments:
|
|
|
|
ResourceName - supplies the resource name
|
|
|
|
ResourceKey - supplies a handle to the resource's cluster registry key
|
|
|
|
ResourceHandle - the resource handle to be supplied with SetResourceStatus
|
|
is called.
|
|
|
|
SetResourceStatus - a routine to call to update status for the resource.
|
|
|
|
LogEvent - a routine to call to log an event for this resource.
|
|
|
|
Return Value:
|
|
|
|
RESID of created resource
|
|
NULL on failure
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
HKEY parametersKey = NULL;
|
|
PDUMMY_RESOURCE resource = NULL;
|
|
RESID resourceId = 0;
|
|
DWORD asyncMode = FALSE;
|
|
DWORD valueSize;
|
|
DWORD valueType;
|
|
DWORD pendingMode = FALSE;
|
|
DWORD opensFail = FALSE;
|
|
DWORD pendingTime = 0;
|
|
|
|
status = ClusterRegOpenKey(ResourceKey,
|
|
L"Parameters",
|
|
KEY_READ,
|
|
¶metersKey);
|
|
|
|
if (status != NO_ERROR) {
|
|
(LogEvent)(
|
|
ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Unable to open parameters key, status %1!u!\n",
|
|
status);
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// Find out if we should just fail the open request.
|
|
//
|
|
valueSize = sizeof(opensFail);
|
|
status = ClusterRegQueryValue(parametersKey,
|
|
DUMMY_PARAMETER_OPENSFAIL,
|
|
&valueType,
|
|
(PBYTE)&opensFail,
|
|
&valueSize);
|
|
if ( status != NO_ERROR ) {
|
|
opensFail = FALSE;
|
|
}
|
|
|
|
if ( opensFail ) {
|
|
ClusterRegCloseKey(parametersKey);
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Find out whether we should run in async failure mode.
|
|
//
|
|
|
|
valueSize = sizeof(asyncMode);
|
|
status = ClusterRegQueryValue(parametersKey,
|
|
DUMMY_PARAMETER_ASYNC,
|
|
&valueType,
|
|
(PBYTE)&asyncMode,
|
|
&valueSize);
|
|
|
|
if (status != NO_ERROR) {
|
|
asyncMode = FALSE;
|
|
}
|
|
|
|
//
|
|
// Find out whether we should return Pending on shutdown.
|
|
//
|
|
|
|
valueSize = sizeof(pendingMode);
|
|
status = ClusterRegQueryValue(parametersKey,
|
|
DUMMY_PARAMETER_PENDING,
|
|
&valueType,
|
|
(PBYTE)&pendingMode,
|
|
&valueSize);
|
|
|
|
if (status != NO_ERROR) {
|
|
pendingMode = FALSE;
|
|
} else {
|
|
//
|
|
// See if there is a defined time period
|
|
//
|
|
valueSize = sizeof(pendingTime);
|
|
status = ClusterRegQueryValue(parametersKey,
|
|
DUMMY_PARAMETER_PENDTIME,
|
|
&valueType,
|
|
(PBYTE)&pendingTime,
|
|
&valueSize);
|
|
}
|
|
|
|
//
|
|
// Now go allocate a resource structure.
|
|
//
|
|
|
|
DummyAcquireResourceLock();
|
|
|
|
resource = DummyAllocateResource(&resourceId);
|
|
|
|
if (resource == NULL) {
|
|
(LogEvent)(
|
|
ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Unable to allocate resource structure\n");
|
|
DummyReleaseResourceLock();
|
|
ClusterRegCloseKey(parametersKey);
|
|
return(0);
|
|
}
|
|
|
|
resource->State = ClusterResourceOffline;
|
|
resource->ParametersKey = parametersKey;
|
|
resource->ResourceId = resourceId;
|
|
|
|
//
|
|
// Copy stuff for returning pending status.
|
|
//
|
|
|
|
resource->ResourceHandle = ResourceHandle;
|
|
|
|
DummyReleaseResourceLock();
|
|
|
|
//
|
|
// Create a TimerThreadWakeup event if needed.
|
|
//
|
|
|
|
if ( pendingMode || asyncMode ) {
|
|
resource->TimerThreadWakeup = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if ( resource->TimerThreadWakeup == NULL ) {
|
|
DummyFreeResource( resource );
|
|
(LogEvent)(
|
|
ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to create timer thread wakeup event\n");
|
|
ClusterRegCloseKey(parametersKey);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
if ( pendingMode ) {
|
|
resource->Flags |= DUMMY_FLAG_PENDING;
|
|
resource->PendTime = pendingTime;
|
|
}
|
|
|
|
if ( asyncMode ) {
|
|
EnterAsyncMode( resource );
|
|
|
|
resource->SignalEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if ( resource->SignalEvent == NULL ) {
|
|
DummyFreeResource( resource );
|
|
(LogEvent)(
|
|
ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to create a timer event\n");
|
|
ClusterRegCloseKey(parametersKey);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
return(resourceId);
|
|
|
|
} // DumbOpen
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
DumbOnline(
|
|
IN RESID ResourceId,
|
|
IN OUT PHANDLE EventHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Online routine for Dummy resource.
|
|
|
|
Arguments:
|
|
|
|
ResourceId - supplies resource id to be brought online
|
|
|
|
EventHandle - supplies a pointer to a handle to signal on error.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
ERROR_RESOURCE_NOT_FOUND if RESID is not valid.
|
|
ERROR_RESOURCE_NOT_AVAILABLE if resource was arbitrated but failed to
|
|
acquire 'ownership'.
|
|
Win32 error code if other failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDUMMY_RESOURCE resource;
|
|
DWORD status = ERROR_SUCCESS;
|
|
DWORD threadId;
|
|
SYSTEMTIME time;
|
|
|
|
(LogEvent)(
|
|
NULL,
|
|
LOG_INFORMATION,
|
|
L"Online Entry\n");
|
|
|
|
DummyAcquireResourceLock();
|
|
|
|
resource = DummyFindResource(ResourceId);
|
|
|
|
if (resource == NULL) {
|
|
status = ERROR_RESOURCE_NOT_FOUND;
|
|
DUMMY_PRINT("DUMMY Online: resource %u not found.\n", PtrToUlong(ResourceId));
|
|
} else {
|
|
if ( resource->TimerType != TimerNotUsed ) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Timer still running!\n");
|
|
DummyReleaseResourceLock();
|
|
return(ERROR_GEN_FAILURE);
|
|
}
|
|
if ( PendingMode( resource ) ) {
|
|
if ( !resource->ThreadHandle ) {
|
|
resource->TimerType = TimerOnlinePending;
|
|
resource->ThreadHandle = CreateThread(
|
|
NULL,
|
|
0,
|
|
DummyTimerThread,
|
|
resource,
|
|
0,
|
|
&threadId
|
|
);
|
|
if ( resource->ThreadHandle == NULL ) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to start timer thread.\n");
|
|
resource->TimerType = TimerNotUsed;
|
|
status = ERROR_GEN_FAILURE;
|
|
} else {
|
|
CloseHandle( resource->ThreadHandle );
|
|
}
|
|
status = ERROR_IO_PENDING;
|
|
}
|
|
if ( AsyncMode(resource) && (status != ERROR_GEN_FAILURE) ) {
|
|
*EventHandle = resource->SignalEvent;
|
|
}
|
|
} else if ( AsyncMode( resource ) ) {
|
|
if ( !ResetEvent( resource->TimerThreadWakeup ) ) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to reset timer wakeup event\n");
|
|
status = ERROR_GEN_FAILURE;
|
|
} else if ( !resource->ThreadHandle ) {
|
|
resource->ThreadHandle = CreateThread(
|
|
NULL,
|
|
0,
|
|
DummyTimerThread,
|
|
resource,
|
|
0,
|
|
&threadId
|
|
);
|
|
|
|
if ( resource->ThreadHandle == NULL ) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to start timer thread.\n");
|
|
status = ERROR_GEN_FAILURE;
|
|
} else {
|
|
CloseHandle(resource->ThreadHandle);
|
|
}
|
|
resource->TimerType = TimerErrorPending;
|
|
}
|
|
//
|
|
// If we are successful, then return our signal event.
|
|
//
|
|
if ( status == ERROR_SUCCESS ) {
|
|
*EventHandle = resource->SignalEvent;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( status == ERROR_SUCCESS ) {
|
|
resource->State = ClusterResourceOnline;
|
|
GetSystemTime(&time);
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online at %1!02d!:%2!02d!:%3!02d!.%4!03d!\n",
|
|
time.wHour,
|
|
time.wMinute,
|
|
time.wSecond,
|
|
time.wMilliseconds);
|
|
} else if ( status == ERROR_IO_PENDING ) {
|
|
resource->State = ClusterResourceOnlinePending;
|
|
}
|
|
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Online Exit\n");
|
|
|
|
|
|
DummyReleaseResourceLock();
|
|
|
|
return(status);
|
|
|
|
} // DumbOnline
|
|
|
|
|
|
|
|
VOID
|
|
WINAPI
|
|
DumbTerminate(
|
|
IN RESID ResourceId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminate routine for Dummy resource.
|
|
|
|
Arguments:
|
|
|
|
ResourceId - supplies resource id to be terminated
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDUMMY_RESOURCE resource;
|
|
|
|
(LogEvent)(
|
|
NULL,
|
|
LOG_INFORMATION,
|
|
L"Terminate Entry\n");
|
|
|
|
DummyAcquireResourceLock();
|
|
|
|
resource = DummyFindResource(ResourceId);
|
|
|
|
if (resource == NULL) {
|
|
DUMMY_PRINT("DUMMY Terminate: resource %u not found\n", PtrToUlong(ResourceId));
|
|
}
|
|
else {
|
|
resource->State = ClusterResourceFailed;
|
|
if ( resource->TimerType != TimerNotUsed ) {
|
|
SetEvent( resource->TimerThreadWakeup );
|
|
}
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Terminated\n");
|
|
}
|
|
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Terminate Exit\n");
|
|
|
|
resource->ThreadHandle = NULL;
|
|
DummyReleaseResourceLock();
|
|
|
|
return;
|
|
|
|
} // DumbTerminate
|
|
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
DumbOffline(
|
|
IN RESID ResourceId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Offline routine for Dummy resource.
|
|
|
|
Arguments:
|
|
|
|
ResourceId - supplies the resource it to be taken offline
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - always successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
DWORD threadId;
|
|
PDUMMY_RESOURCE resource;
|
|
|
|
(LogEvent)(
|
|
NULL,
|
|
LOG_INFORMATION,
|
|
L"Offline Entry\n");
|
|
|
|
DumbTerminate(ResourceId);
|
|
|
|
//
|
|
// Now check if we are to return pending...
|
|
//
|
|
|
|
DummyAcquireResourceLock();
|
|
|
|
resource = DummyFindResource(ResourceId);
|
|
|
|
if (resource == NULL) {
|
|
DUMMY_PRINT("DUMMY Offline: resource %u not found\n", PtrToUlong(ResourceId));
|
|
} else {
|
|
resource->State = ClusterResourceOfflinePending;
|
|
if ( resource->TimerType != TimerNotUsed ) {
|
|
DummyReleaseResourceLock();
|
|
return(ERROR_IO_PENDING);
|
|
}
|
|
resource->State = ClusterResourceOffline;
|
|
if ( PendingMode(resource) ) {
|
|
//CL_ASSERT( resource->ThreadHandle == NULL );
|
|
if ( !ResetEvent( resource->TimerThreadWakeup ) ) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to reset pending wakeup event\n");
|
|
status = ERROR_GEN_FAILURE;
|
|
} else if ( !resource->ThreadHandle ) {
|
|
resource->ThreadHandle = CreateThread(
|
|
NULL,
|
|
0,
|
|
DummyTimerThread,
|
|
resource,
|
|
0,
|
|
&threadId
|
|
);
|
|
|
|
if ( resource->ThreadHandle == NULL ) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"Failed to start pending timer thread.\n");
|
|
status = GetLastError();
|
|
} else {
|
|
CloseHandle( resource->ThreadHandle );
|
|
resource->TimerType = TimerOfflinePending;
|
|
status = ERROR_IO_PENDING;
|
|
resource->State = ClusterResourceOfflinePending;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Offline Exit\n");
|
|
|
|
DummyReleaseResourceLock();
|
|
|
|
return(status);
|
|
|
|
} // DumbOffline
|
|
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
DumbIsAlive(
|
|
IN RESID ResourceId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IsAlive routine for Dummy resource.
|
|
|
|
Arguments:
|
|
|
|
ResourceId - supplies the resource id to be polled.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Resource is alive and well
|
|
|
|
FALSE - Resource is toast.
|
|
|
|
--*/
|
|
|
|
{
|
|
SYSTEMTIME Time;
|
|
PDUMMY_RESOURCE resource;
|
|
BOOLEAN returnValue = FALSE;
|
|
DWORD failed = FALSE;
|
|
DWORD status;
|
|
DWORD ValueType;
|
|
DWORD ValueSize;
|
|
|
|
DummyAcquireResourceLock();
|
|
|
|
resource = DummyFindResource(ResourceId);
|
|
|
|
if (resource == NULL) {
|
|
DUMMY_PRINT("DUMMY IsAlive: resource %u not found.\n", PtrToUlong(ResourceId));
|
|
DummyReleaseResourceLock();
|
|
return(FALSE);
|
|
}
|
|
|
|
GetSystemTime(&Time);
|
|
|
|
ValueSize = sizeof(failed);
|
|
status = ClusterRegQueryValue(resource->ParametersKey,
|
|
DUMMY_PARAMETER_FAILED,
|
|
&ValueType,
|
|
(PBYTE)&failed,
|
|
&ValueSize);
|
|
|
|
if (status != NO_ERROR) {
|
|
if (resource->State == ClusterResourceFailed) {
|
|
failed = TRUE;
|
|
}
|
|
}
|
|
|
|
if (resource->State == ClusterResourceFailed) {
|
|
if (!failed) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Recovered at %1!2d!:%2!02d!:%3!02d!.%4!03d! !!!!\n",
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Time.wSecond,
|
|
Time.wMilliseconds);
|
|
resource->State = ClusterResourceOnline;
|
|
}
|
|
else {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Is Dead at %1!2d!:%2!02d!:%3!02d!.%4!03d!\n",
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Time.wSecond,
|
|
Time.wMilliseconds);
|
|
}
|
|
}
|
|
else if (resource->State == ClusterResourceOnline) {
|
|
if (failed) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Failed at %1!2d!:%2!02d!:%3!02d!.%4!03d! !!!!\n",
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Time.wSecond,
|
|
Time.wMilliseconds);
|
|
resource->State = ClusterResourceFailed;
|
|
}
|
|
else {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Is Alive at %1!2d!:%2!02d!:%3!02d!.%4!03d!\n",
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Time.wSecond,
|
|
Time.wMilliseconds);
|
|
}
|
|
}
|
|
else {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Resource state %1!u! during IsAlive poll at %2!2d!:%3!02d!:%4!02d!.%5!03d! !!!!\n",
|
|
resource->State,
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Time.wSecond,
|
|
Time.wMilliseconds);
|
|
}
|
|
|
|
returnValue = (resource->State == ClusterResourceOnline) ? TRUE : FALSE;
|
|
|
|
DummyReleaseResourceLock();
|
|
|
|
return(returnValue);
|
|
|
|
} // DumbIsAlive
|
|
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
DumbLooksAlive(
|
|
IN RESID ResourceId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
LooksAlive routine for Dummy resource.
|
|
|
|
Arguments:
|
|
|
|
ResourceId - supplies the resource id to be polled.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Resource looks like it is alive and well
|
|
|
|
FALSE - Resource looks like it is toast.
|
|
|
|
--*/
|
|
|
|
{
|
|
SYSTEMTIME Time;
|
|
PDUMMY_RESOURCE resource;
|
|
BOOLEAN returnValue = FALSE;
|
|
DWORD failed = FALSE;
|
|
DWORD status;
|
|
DWORD ValueType;
|
|
DWORD ValueSize;
|
|
|
|
|
|
DummyAcquireResourceLock();
|
|
|
|
resource = DummyFindResource(ResourceId);
|
|
|
|
if (resource == NULL) {
|
|
DUMMY_PRINT("DUMMY LooksAlive: resource %u not found.\n", PtrToUlong(ResourceId));
|
|
DummyReleaseResourceLock();
|
|
return(FALSE);
|
|
}
|
|
|
|
GetSystemTime(&Time);
|
|
|
|
ValueSize = sizeof(failed);
|
|
status = ClusterRegQueryValue(resource->ParametersKey,
|
|
DUMMY_PARAMETER_FAILED,
|
|
&ValueType,
|
|
(PBYTE)&failed,
|
|
&ValueSize);
|
|
|
|
if (status != NO_ERROR) {
|
|
if (resource->State == ClusterResourceFailed) {
|
|
failed = TRUE;
|
|
}
|
|
}
|
|
|
|
if (resource->State == ClusterResourceFailed) {
|
|
if (!failed) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Recovered at %1!2d!:%2!02d!:%3!02d!.%4!03d! !!!!\n",
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Time.wSecond,
|
|
Time.wMilliseconds);
|
|
resource->State = ClusterResourceOnline;
|
|
}
|
|
else {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Looks Dead at %1!2d!:%2!02d!:%3!02d!.%4!03d!\n",
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Time.wSecond,
|
|
Time.wMilliseconds);
|
|
}
|
|
}
|
|
else if (resource->State == ClusterResourceOnline) {
|
|
if (failed) {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Failed at %1!2d!:%2!02d!:%3!02d!.%4!03d! !!!!\n",
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Time.wSecond,
|
|
Time.wMilliseconds);
|
|
resource->State = ClusterResourceFailed;
|
|
}
|
|
else {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Looks Alive at %1!2d!:%2!02d!:%3!02d!.%4!03d!\n",
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Time.wSecond,
|
|
Time.wMilliseconds);
|
|
}
|
|
}
|
|
else {
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Resource state %1!u! during LooksAlive poll at %2!2d!:%3!02d!:%4!02d!.%5!03d! !!!!\n",
|
|
resource->State,
|
|
Time.wHour,
|
|
Time.wMinute,
|
|
Time.wSecond,
|
|
Time.wMilliseconds);
|
|
}
|
|
|
|
returnValue = (resource->State == ClusterResourceOnline) ? TRUE : FALSE;
|
|
|
|
DummyReleaseResourceLock();
|
|
|
|
return(returnValue);
|
|
|
|
} // DumbLooksAlive
|
|
|
|
|
|
|
|
VOID
|
|
WINAPI
|
|
DumbClose(
|
|
IN RESID ResourceId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close routine for Dummy resource.
|
|
|
|
Arguments:
|
|
|
|
ResourceId - supplies resource id to be closed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDUMMY_RESOURCE resource;
|
|
PLIST_ENTRY entry;
|
|
|
|
|
|
DummyAcquireResourceLock();
|
|
|
|
resource = DummyFindResource(ResourceId);
|
|
|
|
if (resource == NULL) {
|
|
DUMMY_PRINT("DUMMY: Close, resource %u not found\n", PtrToUlong(ResourceId));
|
|
}
|
|
else {
|
|
if ( resource->TimerThreadWakeup != NULL ) {
|
|
CloseHandle( resource->TimerThreadWakeup );
|
|
}
|
|
if ( resource->SignalEvent != NULL ) {
|
|
CloseHandle( resource->SignalEvent );
|
|
}
|
|
ClusterRegCloseKey(resource->ParametersKey);
|
|
DummyFreeResource(resource);
|
|
}
|
|
|
|
DummyReleaseResourceLock();
|
|
|
|
(LogEvent)(
|
|
resource->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"Closed.\n");
|
|
|
|
return;
|
|
|
|
} // DumbClose
|
|
|
|
//
|
|
// Define Function Table
|
|
//
|
|
|
|
CLRES_V1_FUNCTION_TABLE( DumbFunctionTable,
|
|
CLRES_VERSION_V1_00,
|
|
Dumb,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
|