950 lines
27 KiB
C
950 lines
27 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 2001 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
Monitor.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Routines for interfacing with the Resource Monitor process
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
John Vert (jvert) 3-Jan-1996
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
#include "fmp.h"
|
|||
|
|
|||
|
//
|
|||
|
// Global data
|
|||
|
//
|
|||
|
|
|||
|
CRITICAL_SECTION FmpMonitorLock;
|
|||
|
|
|||
|
//
|
|||
|
// Local function prototypes
|
|||
|
//
|
|||
|
DWORD
|
|||
|
FmpInitializeResourceMonitorNotify(
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
FmpRmNotifyThread(
|
|||
|
IN LPVOID lpThreadParameter
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
PRESMON
|
|||
|
FmpCreateMonitor(
|
|||
|
LPWSTR DebugPrefix,
|
|||
|
BOOL SeparateMonitor
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Creates a new monitor process and initiates the RPC communication
|
|||
|
with it.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Pointer to the resource monitor structure if successful.
|
|||
|
|
|||
|
NULL otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
#define FM_MAX_RESMON_COMMAND_LINE_SIZE 128
|
|||
|
|
|||
|
SECURITY_ATTRIBUTES Security;
|
|||
|
HANDLE WaitArray[2];
|
|||
|
HANDLE ThreadHandle;
|
|||
|
HANDLE Event = NULL;
|
|||
|
HANDLE FileMapping = NULL;
|
|||
|
STARTUPINFO StartupInfo;
|
|||
|
PROCESS_INFORMATION ProcessInfo;
|
|||
|
PROCESS_INFORMATION DebugInfo;
|
|||
|
BOOL Success;
|
|||
|
WCHAR CommandBuffer[FM_MAX_RESMON_COMMAND_LINE_SIZE];
|
|||
|
PWCHAR resmonCmdLine = CommandBuffer;
|
|||
|
TCHAR DebugLine[512];
|
|||
|
TCHAR *Binding;
|
|||
|
RPC_BINDING_HANDLE RpcBinding;
|
|||
|
DWORD Status;
|
|||
|
PRESMON Monitor;
|
|||
|
DWORD ThreadId;
|
|||
|
DWORD Retry = 0;
|
|||
|
DWORD creationFlags;
|
|||
|
|
|||
|
//
|
|||
|
// Recover any DLL files left impartially upgraded.
|
|||
|
//
|
|||
|
FmpRecoverResourceDLLFiles ();
|
|||
|
|
|||
|
Monitor = LocalAlloc(LMEM_ZEROINIT, sizeof(RESMON));
|
|||
|
if (Monitor == NULL) {
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] Failed to allocate a Monitor structure.\n");
|
|||
|
return(NULL);
|
|||
|
}
|
|||
|
|
|||
|
Monitor->Shutdown = FALSE;
|
|||
|
Monitor->Signature = FMP_RESMON_SIGNATURE;
|
|||
|
|
|||
|
//
|
|||
|
// Create an event and a file mapping object to be passed to
|
|||
|
// the Resource Monitor process. The event is for the Resource
|
|||
|
// Monitor to signal its initialization is complete. The file
|
|||
|
// mapping is for creating the shared memory region between
|
|||
|
// the Resource Monitor and the cluster manager.
|
|||
|
//
|
|||
|
Security.nLength = sizeof(Security);
|
|||
|
Security.lpSecurityDescriptor = NULL;
|
|||
|
Security.bInheritHandle = TRUE;
|
|||
|
Event = CreateEvent(&Security,
|
|||
|
TRUE,
|
|||
|
FALSE,
|
|||
|
NULL);
|
|||
|
if (Event == NULL) {
|
|||
|
Status = GetLastError();
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] Failed to create a ResMon event, error %1!u!.\n",
|
|||
|
Status);
|
|||
|
goto create_failed;
|
|||
|
}
|
|||
|
|
|||
|
Security.nLength = sizeof(Security);
|
|||
|
Security.lpSecurityDescriptor = NULL;
|
|||
|
Security.bInheritHandle = TRUE;
|
|||
|
FileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,
|
|||
|
&Security,
|
|||
|
PAGE_READWRITE,
|
|||
|
0,
|
|||
|
sizeof(MONITOR_STATE),
|
|||
|
NULL);
|
|||
|
if (FileMapping == NULL) {
|
|||
|
Status = GetLastError();
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] File Mapping for ResMon failed, error = %1!u!.\n",
|
|||
|
Status);
|
|||
|
goto create_failed;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create our own (read-only) view of the shared memory section
|
|||
|
//
|
|||
|
Monitor->SharedState = MapViewOfFile(FileMapping,
|
|||
|
FILE_MAP_READ | FILE_MAP_WRITE,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0);
|
|||
|
if (Monitor->SharedState == NULL) {
|
|||
|
Status = GetLastError();
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] Mapping shared state for ResMon failed, error %1!u!.\n",
|
|||
|
Status);
|
|||
|
goto create_failed;
|
|||
|
}
|
|||
|
|
|||
|
ZeroMemory( Monitor->SharedState, sizeof(MONITOR_STATE) );
|
|||
|
if ( !CsDebugResmon && DebugPrefix != NULL && *DebugPrefix != UNICODE_NULL ) {
|
|||
|
Monitor->SharedState->ResmonStop = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// build cmd line for Resource Monitor process
|
|||
|
//
|
|||
|
wsprintf(resmonCmdLine,
|
|||
|
TEXT("resrcmon -e %d -m %d -p %d"),
|
|||
|
Event,
|
|||
|
FileMapping,
|
|||
|
GetCurrentProcessId() );
|
|||
|
if ( CsDebugResmon ) {
|
|||
|
wcscat( resmonCmdLine, L" -d" );
|
|||
|
|
|||
|
if ( CsResmonDebugCmd ) {
|
|||
|
DWORD cmdLineSize = wcslen( resmonCmdLine );
|
|||
|
DWORD debugCmdSize = wcslen( CsResmonDebugCmd );
|
|||
|
|
|||
|
//
|
|||
|
// make sure our static buffer is large enough; 4 includes the
|
|||
|
// space, 2 double quotes and. 5 adds in the terminating NULL.
|
|||
|
//
|
|||
|
if (( cmdLineSize + debugCmdSize ) > ( FM_MAX_RESMON_COMMAND_LINE_SIZE - 4 )) {
|
|||
|
resmonCmdLine = LocalAlloc(LMEM_FIXED,
|
|||
|
( cmdLineSize + debugCmdSize + 5 ) * sizeof( WCHAR ));
|
|||
|
|
|||
|
if ( resmonCmdLine != NULL ) {
|
|||
|
wcscpy( resmonCmdLine, CommandBuffer );
|
|||
|
wcscat( resmonCmdLine, L" \"" );
|
|||
|
wcscat( resmonCmdLine, CsResmonDebugCmd );
|
|||
|
wcscat( resmonCmdLine, L"\"" );
|
|||
|
} else {
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] Unable to allocate space for debug command line\n");
|
|||
|
resmonCmdLine = CommandBuffer;
|
|||
|
}
|
|||
|
} else {
|
|||
|
wcscat( resmonCmdLine, L" \"" );
|
|||
|
wcscat( resmonCmdLine, CsResmonDebugCmd );
|
|||
|
wcscat( resmonCmdLine, L"\"" );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Attempt to start ResMon process.
|
|||
|
//
|
|||
|
retry_resmon_start:
|
|||
|
|
|||
|
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
|||
|
StartupInfo.cb = sizeof(StartupInfo);
|
|||
|
creationFlags = DETACHED_PROCESS; // so ctrl-c won't kill it
|
|||
|
|
|||
|
Success = CreateProcess(NULL,
|
|||
|
resmonCmdLine,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FALSE, // Inherit handles
|
|||
|
creationFlags,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&StartupInfo,
|
|||
|
&ProcessInfo);
|
|||
|
if (!Success) {
|
|||
|
Status = GetLastError();
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] Failed to create resmon process, error %1!u!.\n",
|
|||
|
Status);
|
|||
|
CL_LOGFAILURE(Status);
|
|||
|
goto create_failed;
|
|||
|
} else if ( CsDebugResmon && !CsResmonDebugCmd ) {
|
|||
|
ClRtlLogPrint(LOG_CRITICAL,
|
|||
|
"[FM] Waiting for debugger to connect to resmon process %1!u!\n",
|
|||
|
ProcessInfo.dwProcessId);
|
|||
|
}
|
|||
|
|
|||
|
CloseHandle(ProcessInfo.hThread); // don't need this
|
|||
|
|
|||
|
//
|
|||
|
// Wait for the ResMon process to terminate, or for it to signal
|
|||
|
// its startup event.
|
|||
|
//
|
|||
|
WaitArray[0] = Event;
|
|||
|
WaitArray[1] = ProcessInfo.hProcess;
|
|||
|
Status = WaitForMultipleObjects(2,
|
|||
|
WaitArray,
|
|||
|
FALSE,
|
|||
|
INFINITE);
|
|||
|
if (Status == WAIT_FAILED) {
|
|||
|
Status = GetLastError();
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] Wait for ResMon to start failed, error %1!u!.\n",
|
|||
|
Status);
|
|||
|
goto create_failed;
|
|||
|
}
|
|||
|
|
|||
|
if (Status == ( WAIT_OBJECT_0 + 1 )) {
|
|||
|
if ( ++Retry > 1 ) {
|
|||
|
//
|
|||
|
// The resource monitor terminated prematurely.
|
|||
|
//
|
|||
|
GetExitCodeProcess(ProcessInfo.hProcess, &Status);
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] ResMon terminated prematurely, error %1!u!.\n",
|
|||
|
Status);
|
|||
|
goto create_failed;
|
|||
|
} else {
|
|||
|
goto retry_resmon_start;
|
|||
|
}
|
|||
|
} else {
|
|||
|
//
|
|||
|
// The resource monitor has successfully initialized
|
|||
|
//
|
|||
|
CL_ASSERT(Status == 0);
|
|||
|
Monitor->Process = ProcessInfo.hProcess;
|
|||
|
|
|||
|
//
|
|||
|
// invoke the DebugPrefix process only if we're not already debugging
|
|||
|
// the resmon process
|
|||
|
//
|
|||
|
if ( CsDebugResmon && DebugPrefix && *DebugPrefix != UNICODE_NULL ) {
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] -debugresmon overrides DebugPrefix property\n");
|
|||
|
}
|
|||
|
|
|||
|
if ( !CsDebugResmon && ( DebugPrefix != NULL ) && ( *DebugPrefix != UNICODE_NULL )) {
|
|||
|
|
|||
|
wsprintf(DebugLine, TEXT("%ws -p %d"), DebugPrefix, ProcessInfo.dwProcessId);
|
|||
|
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
|||
|
StartupInfo.cb = sizeof(StartupInfo);
|
|||
|
StartupInfo.lpDesktop = TEXT("WinSta0\\Default");
|
|||
|
|
|||
|
Success = CreateProcess(NULL,
|
|||
|
DebugLine,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
FALSE, // Inherit handles
|
|||
|
CREATE_NEW_CONSOLE,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&StartupInfo,
|
|||
|
&DebugInfo);
|
|||
|
Monitor->SharedState->ResmonStop = FALSE;
|
|||
|
if ( !Success ) {
|
|||
|
Status = GetLastError();
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] ResMon debug start failed, error %1!u!.\n",
|
|||
|
Status);
|
|||
|
} else {
|
|||
|
CloseHandle(DebugInfo.hThread); // don't need this
|
|||
|
CloseHandle(DebugInfo.hProcess); // don't need this
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CloseHandle(Event);
|
|||
|
CloseHandle(FileMapping);
|
|||
|
Event = NULL;
|
|||
|
FileMapping = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Initiate RPC with resource monitor process
|
|||
|
//
|
|||
|
wsprintf(resmonCmdLine, TEXT("resrcmon%d"), ProcessInfo.dwProcessId);
|
|||
|
Status = RpcStringBindingCompose(TEXT("e76ea56d-453f-11cf-bfec-08002be23f2f"),
|
|||
|
TEXT("ncalrpc"),
|
|||
|
NULL,
|
|||
|
resmonCmdLine,
|
|||
|
NULL,
|
|||
|
&Binding);
|
|||
|
if (Status != RPC_S_OK) {
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] ResMon RPC binding compose failed, error %1!u!.\n",
|
|||
|
Status);
|
|||
|
goto create_failed;
|
|||
|
}
|
|||
|
Status = RpcBindingFromStringBinding(Binding, &Monitor->Binding);
|
|||
|
if (Status != RPC_S_OK) {
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] ResMon RPC binding creation failed, error %1!u!.\n",
|
|||
|
Status);
|
|||
|
goto create_failed;
|
|||
|
}
|
|||
|
RpcStringFree(&Binding);
|
|||
|
|
|||
|
//
|
|||
|
// Start notification thread.
|
|||
|
//
|
|||
|
Monitor->NotifyThread = CreateThread(NULL,
|
|||
|
0,
|
|||
|
FmpRmNotifyThread,
|
|||
|
Monitor,
|
|||
|
0,
|
|||
|
&ThreadId);
|
|||
|
|
|||
|
if (Monitor->NotifyThread == NULL) {
|
|||
|
Status = GetLastError();
|
|||
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|||
|
"[FM] Creation of notify thread for ResMon failed, error %1!u!.\n",
|
|||
|
Status);
|
|||
|
goto create_failed;
|
|||
|
}
|
|||
|
|
|||
|
Monitor->RefCount = 2;
|
|||
|
|
|||
|
if ( resmonCmdLine != CommandBuffer ) {
|
|||
|
LocalFree( resmonCmdLine );
|
|||
|
}
|
|||
|
|
|||
|
return(Monitor);
|
|||
|
|
|||
|
create_failed:
|
|||
|
|
|||
|
if ( Monitor->NotifyThread != NULL ) {
|
|||
|
CloseHandle( Monitor->NotifyThread );
|
|||
|
}
|
|||
|
LocalFree( Monitor );
|
|||
|
|
|||
|
if ( FileMapping != NULL ) {
|
|||
|
CloseHandle( FileMapping );
|
|||
|
}
|
|||
|
|
|||
|
if ( Event != NULL ) {
|
|||
|
CloseHandle( Event );
|
|||
|
}
|
|||
|
|
|||
|
if ( resmonCmdLine != CommandBuffer ) {
|
|||
|
LocalFree( resmonCmdLine );
|
|||
|
}
|
|||
|
|
|||
|
SetLastError(Status);
|
|||
|
|
|||
|
return(NULL);
|
|||
|
|
|||
|
} // FmpCreateMonitor
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FmpShutdownMonitor(
|
|||
|
IN PRESMON Monitor
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Performs a clean shutdown of the Resource Monitor process.
|
|||
|
Note that this does not make any changes to the state of
|
|||
|
any resources being monitored by the Resource Monitor, it
|
|||
|
only asks the Resource Monitor to clean up and terminate.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD Status;
|
|||
|
|
|||
|
CL_ASSERT(Monitor != NULL);
|
|||
|
|
|||
|
FmpAcquireMonitorLock();
|
|||
|
|
|||
|
if ( Monitor->Shutdown ) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Monitor->Shutdown = TRUE;
|
|||
|
|
|||
|
FmpReleaseMonitorLock();
|
|||
|
|
|||
|
//
|
|||
|
// RPC to the server process to tell it to shutdown.
|
|||
|
//
|
|||
|
RmShutdownProcess(Monitor->Binding);
|
|||
|
|
|||
|
//
|
|||
|
// Wait for the process to exit so that the monitor fully cleans up the resources if necessary.
|
|||
|
//
|
|||
|
if ( Monitor->Process ) {
|
|||
|
Status = WaitForSingleObject(Monitor->Process, FM_MONITOR_SHUTDOWN_TIMEOUT);
|
|||
|
if ( Status != WAIT_OBJECT_0 ) {
|
|||
|
ClRtlLogPrint(LOG_ERROR,"[FM] Failed to shutdown resource monitor.\n");
|
|||
|
TerminateProcess( Monitor->Process, 1 );
|
|||
|
}
|
|||
|
CloseHandle(Monitor->Process);
|
|||
|
Monitor->Process = NULL;
|
|||
|
}
|
|||
|
|
|||
|
RpcBindingFree(&Monitor->Binding);
|
|||
|
|
|||
|
//
|
|||
|
// Wait for the notify thread to exit, but just a little bit.
|
|||
|
//
|
|||
|
if ( Monitor->NotifyThread ) {
|
|||
|
Status = WaitForSingleObject(Monitor->NotifyThread,
|
|||
|
FM_RPC_TIMEOUT*2); // Increased timeout to try to ensure RPC completes
|
|||
|
if ( Status != WAIT_OBJECT_0 ) {
|
|||
|
; // call removed: Terminate Thread( Monitor->NotifyThread, 1 );
|
|||
|
// Bad call to make since terminating threads on NT can cause real problems.
|
|||
|
}
|
|||
|
CloseHandle(Monitor->NotifyThread);
|
|||
|
Monitor->NotifyThread = NULL;
|
|||
|
}
|
|||
|
//
|
|||
|
// Clean up shared memory mapping
|
|||
|
//
|
|||
|
UnmapViewOfFile(Monitor->SharedState);
|
|||
|
|
|||
|
if ( InterlockedDecrement(&Monitor->RefCount) == 0 ) {
|
|||
|
PVOID caller, callersCaller;
|
|||
|
RtlGetCallersAddress(
|
|||
|
&caller,
|
|||
|
&callersCaller );
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[FMY] Freeing monitor structure (1) %1!lx!, caller %2!lx!, callerscaller %3!lx!\n",
|
|||
|
Monitor, caller, callersCaller );
|
|||
|
LocalFree(Monitor);
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // FmpShutdownMonitor
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
FmpRmNotifyThread(
|
|||
|
IN LPVOID lpThreadParameter
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the thread that receives resource monitor notifications.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
lpThreadParameter - Pointer to resource monitor structure.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PRESMON Monitor;
|
|||
|
PRESMON NewMonitor;
|
|||
|
RM_NOTIFY_KEY NotifyKey;
|
|||
|
DWORD NotifyEvent;
|
|||
|
DWORD Status;
|
|||
|
CLUSTER_RESOURCE_STATE CurrentState;
|
|||
|
BOOL Success;
|
|||
|
|
|||
|
Monitor = lpThreadParameter;
|
|||
|
|
|||
|
//
|
|||
|
// Loop forever picking up resource monitor notifications.
|
|||
|
// When the resource monitor returns FALSE, it indicates
|
|||
|
// that shutdown is occurring.
|
|||
|
//
|
|||
|
do {
|
|||
|
try {
|
|||
|
Success = RmNotifyChanges(Monitor->Binding,
|
|||
|
&NotifyKey,
|
|||
|
&NotifyEvent,
|
|||
|
(LPDWORD)&CurrentState);
|
|||
|
} except (I_RpcExceptionFilter(RpcExceptionCode())) {
|
|||
|
//
|
|||
|
// RPC communications failure, treat it as a shutdown.
|
|||
|
//
|
|||
|
Status = GetExceptionCode();
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[FM] NotifyChanges got an RPC failure, %1!u!.\n",
|
|||
|
Status);
|
|||
|
Success = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (Success) {
|
|||
|
Success = FmpPostNotification(NotifyKey, NotifyEvent, CurrentState);
|
|||
|
} else {
|
|||
|
//
|
|||
|
// If we are shutting down... then this is okay.
|
|||
|
//
|
|||
|
if ( FmpShutdown ||
|
|||
|
Monitor->Shutdown ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We will try to start a new resource monitor. If this fails,
|
|||
|
// then shutdown the cluster service.
|
|||
|
//
|
|||
|
ClRtlLogPrint(LOG_ERROR,
|
|||
|
"[FM] Resource monitor terminated!\n");
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_ERROR,
|
|||
|
"[FM] Last resource monitor state: %1!u!, resource %2!u!.\n",
|
|||
|
Monitor->SharedState->State,
|
|||
|
Monitor->SharedState->ActiveResource);
|
|||
|
|
|||
|
CsLogEvent(LOG_UNUSUAL, FM_EVENT_RESMON_DIED);
|
|||
|
|
|||
|
//
|
|||
|
// Use a worker thread to start new resource monitor(s).
|
|||
|
//
|
|||
|
if (FmpCreateMonitorRestartThread(Monitor))
|
|||
|
CsInconsistencyHalt(ERROR_INVALID_STATE);
|
|||
|
}
|
|||
|
|
|||
|
} while ( Success );
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_NOISE,"[FM] RmNotifyChanges returned\n");
|
|||
|
|
|||
|
if ( InterlockedDecrement( &Monitor->RefCount ) == 0 ) {
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[FMY] Freeing monitor structure (2) %1!lx!\n",
|
|||
|
Monitor );
|
|||
|
LocalFree( Monitor );
|
|||
|
}
|
|||
|
|
|||
|
return(0);
|
|||
|
|
|||
|
} // FmpRmNotifyThread
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
FmpFindMonitorResource(
|
|||
|
IN PRESMON OldMonitor,
|
|||
|
IN PMONITOR_RESOURCE_ENUM *PtrEnumResource,
|
|||
|
IN PFM_RESOURCE Resource,
|
|||
|
IN LPCWSTR Name
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Finds all resources that were managed by the old resource monitor and
|
|||
|
starts them under the new resource monitor. Or adds them to the list
|
|||
|
of resources to be restarted.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
OldMonitor - pointer to the old resource monitor structure.
|
|||
|
|
|||
|
PtrEnumResource - pointer to a pointer to a resource enum structure.
|
|||
|
|
|||
|
Resource - the current resource being enumerated.
|
|||
|
|
|||
|
Name - name of the current resource.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - if we should continue enumeration.
|
|||
|
FALSE - otherwise.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
Nothing in the old resource monitor structure should be used.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
BOOL returnNow = FALSE;
|
|||
|
PMONITOR_RESOURCE_ENUM enumResource = *PtrEnumResource;
|
|||
|
PMONITOR_RESOURCE_ENUM newEnumResource;
|
|||
|
DWORD dwOldBlockingFlag;
|
|||
|
|
|||
|
if ( Resource->Monitor == OldMonitor ) {
|
|||
|
if ( enumResource->fCreateMonitors == FALSE ) goto skip_monitor_creation;
|
|||
|
|
|||
|
//
|
|||
|
// If this is not the quorum resource and it is blocking the
|
|||
|
// quorum resource, then fix it up now.
|
|||
|
//
|
|||
|
|
|||
|
dwOldBlockingFlag = InterlockedExchange( &Resource->BlockingQuorum, 0 );
|
|||
|
if ( dwOldBlockingFlag ) {
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[FM] RestartMonitor: call InterlockedDecrement on gdwQuoBlockingResources, Resource %1!ws!\n",
|
|||
|
OmObjectId(Resource));
|
|||
|
InterlockedDecrement(&gdwQuoBlockingResources);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the resource had been previously create in Resmon, then recreate
|
|||
|
// it with a new resource monitor.
|
|||
|
//
|
|||
|
if ( Resource->Flags & RESOURCE_CREATED ) {
|
|||
|
// Note - this will create a new resource monitor as needed.
|
|||
|
status = FmpRmCreateResource(Resource);
|
|||
|
if ( status != ERROR_SUCCESS ) {
|
|||
|
ClRtlLogPrint(LOG_ERROR,"[FM] Failed to restart resource %1!ws!. Error %2!u!.\n",
|
|||
|
Name, status );
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
} else {
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
} else {
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
skip_monitor_creation:
|
|||
|
//
|
|||
|
// If we successfully recreated a resource monitor, then add it to the
|
|||
|
// list of resources to indicate failure.
|
|||
|
//
|
|||
|
if ( enumResource->CurrentIndex >= enumResource->EntryCount ) {
|
|||
|
newEnumResource = LocalReAlloc( enumResource,
|
|||
|
MONITOR_RESOURCE_SIZE( enumResource->EntryCount +
|
|||
|
ENUM_GROW_SIZE ),
|
|||
|
LMEM_MOVEABLE );
|
|||
|
if ( newEnumResource == NULL ) {
|
|||
|
ClRtlLogPrint(LOG_ERROR,
|
|||
|
"[FM] Failed re-allocating resource enum to restart resource monitor!\n");
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
enumResource = newEnumResource;
|
|||
|
enumResource->EntryCount += ENUM_GROW_SIZE;
|
|||
|
*PtrEnumResource = newEnumResource;
|
|||
|
}
|
|||
|
|
|||
|
enumResource->Entry[enumResource->CurrentIndex] = Resource;
|
|||
|
++enumResource->CurrentIndex;
|
|||
|
|
|||
|
return(TRUE);
|
|||
|
|
|||
|
} // FmpFindMonitorResource
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
FmpRestartMonitor(
|
|||
|
PRESMON OldMonitor
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Creates a new monitor process and initiates the RPC communication
|
|||
|
with it. Restarts all resources that were attached to the old monitor
|
|||
|
process.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
OldMonitor - pointer to the old resource monitor structure.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if successful.
|
|||
|
|
|||
|
FALSE otherwise.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
The old monitor structure is deallocated when done.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD enumSize;
|
|||
|
DWORD i;
|
|||
|
DWORD status;
|
|||
|
PMONITOR_RESOURCE_ENUM enumResource;
|
|||
|
PFM_RESOURCE resource;
|
|||
|
DWORD dwOldBlockingFlag;
|
|||
|
|
|||
|
FmpAcquireMonitorLock();
|
|||
|
|
|||
|
if ( FmpShutdown ) {
|
|||
|
FmpReleaseMonitorLock();
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
enumSize = MONITOR_RESOURCE_SIZE( ENUM_GROW_SIZE );
|
|||
|
enumResource = LocalAlloc( LMEM_ZEROINIT, enumSize );
|
|||
|
if ( enumResource == NULL ) {
|
|||
|
ClRtlLogPrint(LOG_ERROR,
|
|||
|
"[FM] Failed allocating resource enum to restart resource monitor!\n");
|
|||
|
FmpReleaseMonitorLock();
|
|||
|
CsInconsistencyHalt(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
enumResource->EntryCount = ENUM_GROW_SIZE;
|
|||
|
enumResource->CurrentIndex = 0;
|
|||
|
enumResource->fCreateMonitors = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Enumerate all resources controlled by the old resource monitor so that we can invoke the
|
|||
|
// handlers registered for those resources. Both preoffline and postoffline handlers are
|
|||
|
// invoked prior to monitor shutdown so that the assumption made about underlying resource
|
|||
|
// access (such as quorum disk access) remain valid in a graceful monitor shutdown case.
|
|||
|
// We would issue a specific shutdown command in the case of a graceful shutdown occurring
|
|||
|
// as a part of resource DLL upgrade.
|
|||
|
//
|
|||
|
OmEnumObjects( ObjectTypeResource,
|
|||
|
(OM_ENUM_OBJECT_ROUTINE)FmpFindMonitorResource,
|
|||
|
OldMonitor,
|
|||
|
&enumResource );
|
|||
|
|
|||
|
for ( i = 0; i < enumResource->CurrentIndex; i++ ) {
|
|||
|
resource = enumResource->Entry[i];
|
|||
|
if ( ( resource->PersistentState == ClusterResourceOnline ) &&
|
|||
|
( resource->Group->OwnerNode == NmLocalNode ) ) {
|
|||
|
OmNotifyCb( resource, NOTIFY_RESOURCE_PREOFFLINE );
|
|||
|
OmNotifyCb( resource, NOTIFY_RESOURCE_POSTOFFLINE );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
FmpShutdownMonitor( OldMonitor );
|
|||
|
|
|||
|
if ( FmpDefaultMonitor == OldMonitor ) {
|
|||
|
FmpDefaultMonitor = FmpCreateMonitor(NULL, FALSE);
|
|||
|
if ( FmpDefaultMonitor == NULL ) {
|
|||
|
LocalFree( enumResource );
|
|||
|
FmpReleaseMonitorLock();
|
|||
|
CsInconsistencyHalt(GetLastError());
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
enumResource->CurrentIndex = 0;
|
|||
|
enumResource->fCreateMonitors = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Enumerate all resources controlled by the old resource monitor,
|
|||
|
// and connect them into the new resource monitor.
|
|||
|
//
|
|||
|
OmEnumObjects( ObjectTypeResource,
|
|||
|
(OM_ENUM_OBJECT_ROUTINE)FmpFindMonitorResource,
|
|||
|
OldMonitor,
|
|||
|
&enumResource );
|
|||
|
|
|||
|
//
|
|||
|
// First set each resource in the list to the Offline state.
|
|||
|
//
|
|||
|
for ( i = 0; i < enumResource->CurrentIndex; i++ ) {
|
|||
|
resource = enumResource->Entry[i];
|
|||
|
//
|
|||
|
// If the resource is owned by the local system, then do it.
|
|||
|
//
|
|||
|
if ( resource->Group->OwnerNode == NmLocalNode ) {
|
|||
|
resource->State = ClusterResourceOffline;
|
|||
|
|
|||
|
//
|
|||
|
// If this is not the quorum resource and it is blocking the
|
|||
|
// quorum resource, then fix it up now.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
dwOldBlockingFlag = InterlockedExchange( &resource->BlockingQuorum, 0 );
|
|||
|
if ( dwOldBlockingFlag ) {
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[FM] RestartMonitor: call InterlockedDecrement on gdwQuoBlockingResources, Resource %1!ws!\n",
|
|||
|
OmObjectId(resource));
|
|||
|
InterlockedDecrement(&gdwQuoBlockingResources);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find the quorum resource - if present bring online first.
|
|||
|
//
|
|||
|
for ( i = 0; i < enumResource->CurrentIndex; i++ ) {
|
|||
|
resource = enumResource->Entry[i];
|
|||
|
//
|
|||
|
// If the resource is owned by the local system and is the
|
|||
|
// quorum resource, then do it.
|
|||
|
//
|
|||
|
if ( (resource->Group->OwnerNode == NmLocalNode) &&
|
|||
|
resource->QuorumResource ) {
|
|||
|
FmpRestartResourceTree( resource );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now restart the rest of the resources in the list.
|
|||
|
//
|
|||
|
for ( i = 0; i < enumResource->CurrentIndex; i++ ) {
|
|||
|
resource = enumResource->Entry[i];
|
|||
|
//
|
|||
|
// If the resource is owned by the local system, then do it.
|
|||
|
//
|
|||
|
if ( (resource->Group->OwnerNode == NmLocalNode) &&
|
|||
|
!resource->QuorumResource ) {
|
|||
|
FmpRestartResourceTree( resource );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
FmpReleaseMonitorLock();
|
|||
|
|
|||
|
//
|
|||
|
// Don't delete the old monitor block until we've reset the resources
|
|||
|
// to point to the new resource monitor block.
|
|||
|
// Better to get an RPC failure, rather than some form of ACCVIO.
|
|||
|
//
|
|||
|
LocalFree( enumResource );
|
|||
|
|
|||
|
if ( InterlockedDecrement( &OldMonitor->RefCount ) == 0 ) {
|
|||
|
#if 0
|
|||
|
PVOID caller, callersCaller;
|
|||
|
RtlGetCallersAddress(
|
|||
|
&caller,
|
|||
|
&callersCaller );
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[FMY] Freeing monitor structure (3) %1!lx!, caller %2!lx!, callerscaller %3!lx!\n",
|
|||
|
OldMonitor, caller, callersCaller );
|
|||
|
#endif
|
|||
|
LocalFree( OldMonitor );
|
|||
|
}
|
|||
|
|
|||
|
return(TRUE);
|
|||
|
|
|||
|
} // FmpRestartMonitor
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/****
|
|||
|
@func DWORD | FmpCreateMonitorRestartThread| This creates a new
|
|||
|
thread to restart a monitor.
|
|||
|
|
|||
|
@parm IN PRESMON | pMonitor| Pointer to the resource monitor that n
|
|||
|
needs to be restarted.
|
|||
|
|
|||
|
@comm A monitor needs to be started in a separate thread as it
|
|||
|
decrements the gquoblockingrescount for resources therein.
|
|||
|
This cannot be done by fmpworkerthread because that causes
|
|||
|
deadlocks if other items, like failure handling, being
|
|||
|
processed by the fmpworkerthread are waiting for work that
|
|||
|
will done by the items, like restart monitor, still in queue.
|
|||
|
|
|||
|
@rdesc Returns a result code. ERROR_SUCCESS on success.
|
|||
|
|
|||
|
****/
|
|||
|
DWORD FmpCreateMonitorRestartThread(
|
|||
|
IN PRESMON pMonitor
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
HANDLE hThread = NULL;
|
|||
|
DWORD dwThreadId;
|
|||
|
DWORD dwStatus = ERROR_SUCCESS;
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[FM] FmpCreateMonitorRestartThread: Entry\r\n");
|
|||
|
|
|||
|
//reference the resource
|
|||
|
//the thread will dereference it
|
|||
|
InterlockedIncrement( &pMonitor->RefCount );
|
|||
|
|
|||
|
hThread = CreateThread( NULL, 0, FmpRestartMonitor,
|
|||
|
pMonitor, 0, &dwThreadId );
|
|||
|
|
|||
|
if ( hThread == NULL )
|
|||
|
{
|
|||
|
dwStatus = GetLastError();
|
|||
|
CL_UNEXPECTED_ERROR(dwStatus);
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
FnExit:
|
|||
|
//do general cleanup
|
|||
|
if (hThread)
|
|||
|
CloseHandle(hThread);
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[FM] FmpCreateMonitorRestartThread: Exit, status %1!u!\r\n",
|
|||
|
dwStatus);
|
|||
|
|
|||
|
return(dwStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|