Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1496 lines
44 KiB

/*++
Copyright (c) 1995-1997 Microsoft Corporation
Module Name:
resmon.c
Abstract:
Startup and initialization portion of the Cluster Resource Monitor
Author:
John Vert (jvert) 30-Nov-1995
Revision History:
Sivaprasad Padisetty (sivapad) 06-18-1997 Added the COM support
--*/
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "resmonp.h"
#include "stdio.h"
#include "stdlib.h"
#include "clusverp.h"
#ifdef COMRES
#include "comres_i.c"
#endif
#define RESMON_MODULE RESMON_MODULE_RESMON
//
// Global data
//
CRITICAL_SECTION RmpListLock;
LOCK_INFO RmpListPPrevPrevLock;
LOCK_INFO RmpListPrevPrevLock;
LOCK_INFO RmpListPrevLock;
LOCK_INFO RmpListLastLock;
LOCK_INFO RmpListLastUnlock;
LOCK_INFO RmpListPrevUnlock;
LOCK_INFO RmpListPrevPrevUnlock;
LOCK_INFO RmpListPPrevPrevUnlock;
CRITICAL_SECTION RmpMonitorStateLock;
PMONITOR_STATE RmpSharedState = NULL;
HANDLE RmpInitEvent = NULL;
HANDLE RmpFileMapping = NULL;
HANDLE RmpClusterProcess = NULL;
HKEY RmpResourcesKey = NULL;
HKEY RmpResTypesKey = NULL;
HCLUSTER RmpHCluster = NULL;
HANDLE RmpWaitArray[MAX_THREADS];
HANDLE RmpRewaitEvent = NULL;
DWORD RmpNumberOfThreads = 0;
BOOL RmpDebugger = FALSE;
BOOL RmpCrashed = FALSE;
LPTOP_LEVEL_EXCEPTION_FILTER lpfnOriginalExceptionFilter = NULL;
BOOL g_fRmpClusterProcessCrashed = FALSE;
DWORD g_dwDebugLogLevel = 0; // Controls spew to debugger
PWCHAR RmonStates[] = {
L"", // Initializing
L"", // Idle
L"Starting",
L"Initializing",
L"Online",
L"Offline",
L"Shutdown",
L"Deleteing",
L"IsAlivePoll",
L"LooksAlivePoll",
L"Arbitrate",
L"Release"
L"ResourceControl",
L"ResourceTypeControl",
0 };
//
// Prototypes local to this module
//
DWORD
RmpInitialize(
VOID
);
VOID
RmpCleanup(
VOID
);
VOID
RmpParseArgs(
int argc,
wchar_t *argv[],
OUT LPDWORD pClussvcProcessId,
OUT HANDLE* pClussvcFileMapping,
OUT HANDLE* pClussvcInitEvent,
OUT LPWSTR* pDebuggerCommand
);
RPC_STATUS
ResmonRpcConnectCallback(
IN RPC_IF_ID * Interface,
IN void * Context
);
LONG
RmpExceptionFilter(
IN PEXCEPTION_POINTERS ExceptionInfo
)
/*++
Routine Description:
Top level exception handler for the resource monitor process.
Currently this just exits immediately and assumes that the
cluster service will notice and clean up the mess.
Arguments:
ExceptionInfo - Supplies the exception information
Return Value:
None.
--*/
{
DWORD code = 0;
if ( !RmpCrashed ) {
RmpCrashed = TRUE;
code = ExceptionInfo->ExceptionRecord->ExceptionCode;
ClRtlLogPrint( LOG_CRITICAL, "[RM] Exception. Code = 0x%1!lx!, Address = 0x%2!p!\n",
ExceptionInfo->ExceptionRecord->ExceptionCode,
ExceptionInfo->ExceptionRecord->ExceptionAddress);
ClRtlLogPrint( LOG_CRITICAL, "[RM] Exception parameters: %1!lx!, %2!lx!, %3!lx!, %4!lx!\n",
ExceptionInfo->ExceptionRecord->ExceptionInformation[0],
ExceptionInfo->ExceptionRecord->ExceptionInformation[1],
ExceptionInfo->ExceptionRecord->ExceptionInformation[2],
ExceptionInfo->ExceptionRecord->ExceptionInformation[3]);
CL_LOGFAILURE(ExceptionInfo->ExceptionRecord->ExceptionCode);
if (lpfnOriginalExceptionFilter)
lpfnOriginalExceptionFilter(ExceptionInfo);
}
//
// Dump an exception report
//
GenerateExceptionReport(ExceptionInfo);
//
// Try to dump the resource and the resource state
//
try {
PRESOURCE resource;
DWORD state = 0;
resource = (PRESOURCE)RmpSharedState->ActiveResource;
if ( state <= RmonResourceTypeControl ) {
state =RmpSharedState->State;
}
ClRtlLogPrint( LOG_CRITICAL, "[RM] Active Resource = %1!08LX!\n",
resource );
ClRtlLogPrint( LOG_CRITICAL, "[RM] Resource State is %1!u!, \"%2!ws!\"\n",
RmpSharedState->State,
RmonStates[RmpSharedState->State] );
if ( resource ) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Resource name is %1!ws!\n",
resource->ResourceName );
ClRtlLogPrint( LOG_CRITICAL, "[RM] Resource type is %1!ws!\n",
resource->ResourceType );
}
} except ( EXCEPTION_EXECUTE_HANDLER ) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Exception %1!08LX! while dumping state for resource!\n",
GetExceptionCode());
}
if ( code == 0xC0000194 ) {
DumpCriticalSection( (PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[0] );
}
if ( IsDebuggerPresent()) {
return(EXCEPTION_CONTINUE_SEARCH);
} else {
#if !CLUSTER_BETA
// terminate only when product ships
TerminateProcess( GetCurrentProcess(),
ExceptionInfo->ExceptionRecord->ExceptionCode );
#endif
return(EXCEPTION_CONTINUE_SEARCH);
}
}
int _cdecl
wmain (argc, argv)
int argc;
wchar_t *argv[];
{
PVOID EventList;
DWORD Status;
HANDLE ResourceId;
CLUSTER_RESOURCE_STATE ResourceState;
WCHAR rpcEndpoint[80];
HKEY ClusterKey;
BOOL Inited = FALSE;
BOOL comInited = FALSE;
BOOL bSuccess;
HANDLE ClussvcFileMapping, ClussvcInitEvent;
DWORD ClussvcProcessId;
LPWSTR debuggerCommand = NULL;
LPWSTR lpszDebugLogLevel;
LPWSTR pResmonRpcEndpointName = NULL;
//
// Initialize the Cluster Rtl routines.
//
lpszDebugLogLevel = _wgetenv(L"ClusterLogLevel");
if (lpszDebugLogLevel != NULL) {
// Added test to keep prefast happy.
if ( swscanf(lpszDebugLogLevel, L"%u", &g_dwDebugLogLevel ) == 0 ) {
g_dwDebugLogLevel = 0;
}
}
if ( (Status = ClRtlInitialize( FALSE, &g_dwDebugLogLevel )) != ERROR_SUCCESS ) {
ClRtlLogPrint(LOG_CRITICAL,
"[RM] Failed to initialize Cluster RTL, error %1!u!.\n",
Status);
goto init_failure;
}
ClRtlInitWmi(NULL);
Inited = TRUE;
//
// Set our unhandled exception filter so that if anything horrible
// goes wrong, we can exit immediately.
//
lpfnOriginalExceptionFilter = SetUnhandledExceptionFilter(RmpExceptionFilter);
//
// Parse the input arguments.
//
RmpParseArgs(argc, argv,
&ClussvcProcessId,
&ClussvcFileMapping,
&ClussvcInitEvent,
&debuggerCommand);
if ((ClussvcInitEvent == NULL) ||
(ClussvcFileMapping == NULL) ||
(ClussvcProcessId == 0)) {
//
// All of these arguments are required.
//
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to parse required parameter.\n");
Status = ERROR_INVALID_PARAMETER;
goto init_failure;
}
//
// We do not want to create resmon process with InheritHandles flag.
// So resmon parameters got changed. We no longer pass handles valid in
// the context of resmon.
//
//
// First, convert ProcessId into ProcessHandle
//
RmpClusterProcess = OpenProcess(PROCESS_ALL_ACCESS,
FALSE, // Don't inherit
ClussvcProcessId);
if (RmpClusterProcess == NULL) {
Status = GetLastError();
ClRtlLogPrint( LOG_CRITICAL, "[RM] OpenProcess for %1!x! process failed, error %2!u!.\n",
RmpClusterProcess, Status);
goto init_failure;
}
//
// Now Dup the handles from ClusSvc to Resmon
//
bSuccess = DuplicateHandle(
RmpClusterProcess, // Source Process
ClussvcInitEvent, // Source Handle
GetCurrentProcess(),// Target Process
&RmpInitEvent, // Target Handle
0, // DUPLICATE_SAME_ACCESS
FALSE, // Don't inherit
DUPLICATE_SAME_ACCESS);
if (!bSuccess) {
Status = GetLastError();
ClRtlLogPrint( LOG_CRITICAL, "[RM] Dup InitEvent handle %1!x! failed, error %2!u!.\n",
ClussvcInitEvent, Status);
goto init_failure;
}
bSuccess = DuplicateHandle(
RmpClusterProcess, // Source Process
ClussvcFileMapping, // Source Handle
GetCurrentProcess(),// Target Process
&RmpFileMapping, // Target Handle
0, // DUPLICATE_SAME_ACCESS
FALSE, // Don't inherit
DUPLICATE_SAME_ACCESS);
if (!bSuccess) {
Status = GetLastError();
ClRtlLogPrint( LOG_CRITICAL, "[RM] Dup FileMapping handle %1!x! failed, error %2!u!.\n",
ClussvcFileMapping, Status);
goto init_failure;
}
if ( debuggerCommand ) {
//
// if -d was specified, then check if the optional command arg was
// specified. If not, wait for a debugger to be attached
// external. Otherwise, append the PID to the passed command and call
// CreateProcess on it.
//
if ( *debuggerCommand == UNICODE_NULL ) {
while ( !IsDebuggerPresent()) {
Sleep( 1000 );
}
} else {
// Largest possible 64 bit number is 20 spaces (in decimal), + 4 for " -p ".
#define MAX_PID_CCH_LEN 24
STARTUPINFOW startupInfo;
PROCESS_INFORMATION processInfo;
DWORD cmdLength;
PWCHAR dbgCmdLine;
cmdLength = wcslen( debuggerCommand );
cmdLength += MAX_PID_CCH_LEN;
dbgCmdLine = LocalAlloc( LMEM_FIXED, cmdLength * sizeof( WCHAR ));
if ( dbgCmdLine != NULL ) {
dbgCmdLine [ cmdLength - 1 ] = UNICODE_NULL;
_snwprintf( dbgCmdLine,
cmdLength - 1, // Include space for NULL
L"%ws -p %d",
debuggerCommand,
GetCurrentProcessId() );
ClRtlLogPrint(LOG_NOISE, "[RM] Starting debugger process: %1!ws!\n", dbgCmdLine );
//
// Attempt to attach debugger to us
//
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
bSuccess = CreateProcessW(NULL,
dbgCmdLine,
NULL,
NULL,
FALSE, // Inherit handles
DETACHED_PROCESS, // so ctrl-c won't kill it
NULL,
NULL,
&startupInfo,
&processInfo);
if (!bSuccess) {
Status = GetLastError();
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] Failed to create debugger process, error %1!u!.\n",
Status);
}
CloseHandle(processInfo.hThread); // don't need these
CloseHandle(processInfo.hProcess);
LocalFree( dbgCmdLine );
} else {
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] Failed to alloc memory for debugger command line, error %1!u!.\n",
GetLastError());
}
}
}
//
// init COM for netname
//
Status = CoInitializeEx( NULL, COINIT_DISABLE_OLE1DDE | COINIT_MULTITHREADED );
if ( !SUCCEEDED( Status )) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Couldn't init COM %1!08X!\n", Status );
goto init_failure;
}
comInited = TRUE;
ClRtlLogPrint( LOG_NOISE, "[RM] Main: Initializing.\r\n");
//
// Initialize the resource monitor.
//
Status = RmpInitialize();
if ( Status != ERROR_SUCCESS ) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to initialize, error %1!u!.\n",
Status);
goto init_failure;
}
RmpSharedState = MapViewOfFile(RmpFileMapping,
FILE_MAP_WRITE,
0,
0,
0);
if (RmpSharedState == NULL) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to init shared state, error %1!u!.\n",
Status = GetLastError());
goto init_failure;
}
CloseHandle(RmpFileMapping);
RmpFileMapping = NULL;
GetSystemTimeAsFileTime((PFILETIME)&RmpSharedState->LastUpdate);
RmpSharedState->State = RmonInitializing;
RmpSharedState->ActiveResource = NULL;
if ( RmpSharedState->ResmonStop ) {
// If ResmonStop is set to TRUE, then a debugger should be attached
RmpDebugger = TRUE;
}
//
// Connect to local cluster and open Resources key.
//
RmpHCluster = OpenCluster(NULL);
if (RmpHCluster == NULL) {
Status = GetLastError();
ClRtlLogPrint( LOG_CRITICAL, "[RM] Error opening cluster, error %1!u!.\n",
Status);
goto init_failure;
}
ClusterKey = GetClusterKey(RmpHCluster, KEY_READ);
if (ClusterKey == NULL) {
Status = GetLastError();
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to open the cluster key, error %1!u!.\n",
Status);
goto init_failure;
}
Status = ClusterRegOpenKey(ClusterKey,
CLUSREG_KEYNAME_RESOURCES,
KEY_READ,
&RmpResourcesKey);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to open Resources cluster registry key, error %1!u!.\n",
Status);
goto init_failure;
}
Status = ClusterRegOpenKey(ClusterKey,
CLUSREG_KEYNAME_RESOURCE_TYPES,
KEY_READ,
&RmpResTypesKey);
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to open ResourceTypes cluster registry key, error %1!u!.\n",
Status);
goto init_failure;
}
//
// The Wait Count identifies the number of events the main thread will
// wait for. This is the notification event, the Cluster Service Process
// plus each event list thread. We start at 2 because the first two entries
// are fixed - for the notification event and the Cluster Service process.
//
RmpNumberOfThreads = 2;
//
// Create an event to be signaled whenever we add a new thread.
//
RmpRewaitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( RmpRewaitEvent == NULL ) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to create rewait event, error %1!u!.\n",
Status = GetLastError());
goto init_failure;
}
//
// Create the first event list, and start a polling thread.
//
EventList = RmpCreateEventList();
if (EventList == NULL) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to create event list, error %1!u!.\n",
Status = GetLastError());
goto init_failure;
}
//
// Register the protocol
//
Status = RpcServerUseProtseqW(L"ncalrpc",
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
NULL);
if (Status != RPC_S_OK) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to initialize RPC interface, error %1!u!.\n",
Status);
goto init_failure;
}
//
// Get the dynamic endpoint name.
//
Status = RmpGetDynamicEndpointName( &pResmonRpcEndpointName );
if (Status != RPC_S_OK) {
ClRtlLogPrint(LOG_CRITICAL,
"[RM] Unable to obtain dynamic EP name, status %1!u!.\n",
Status);
goto init_failure;
}
//
// Save the dynamic endpoint name to the registry
//
Status = RmpSaveDynamicEndpointName ( pResmonRpcEndpointName );
RpcStringFreeW ( &pResmonRpcEndpointName );
if (Status != ERROR_SUCCESS) {
ClRtlLogPrint(LOG_CRITICAL,
"[RM] Unable to save dynamic EP name, status %1!u!.\n",
Status);
goto init_failure;
}
//
// Use NTLM as RPC authentication package.
//
Status = RpcServerRegisterAuthInfo(NULL,
RPC_C_AUTHN_WINNT,
NULL,
NULL);
if (Status != RPC_S_OK) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to register RPC auth info, error %1!u!.\n",
Status);
goto init_failure;
}
//
// Register the interface
//
Status = RpcServerRegisterIfEx(s_resmon_v2_0_s_ifspec,
NULL,
NULL,
0, // No need to set RPC_IF_ALLOW_SECURE_ONLY if security callback
// is specified. If security callback is specified, RPC
// will reject unauthenticated requests without invoking
// callback. This is the info obtained from RpcDev. See
// Windows Bug 572035.
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
ResmonRpcConnectCallback );
if (Status != RPC_S_OK) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to register RPC interface, error %1!u!.\n",
Status);
goto init_failure;
}
Status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
if (Status != RPC_S_OK) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to set RPC server listening, error %1!u!.\n",
Status);
goto init_failure;
}
//
// Set our unhandled exception filter so that if anything horrible
// goes wrong, we can exit immediately.
//
lpfnOriginalExceptionFilter = SetUnhandledExceptionFilter(RmpExceptionFilter);
//
// Set the event to indicate that our initialization is complete.
// This event is passed on the command line.
//
if (!SetEvent(RmpInitEvent)) {
ClRtlLogPrint( LOG_CRITICAL, "[RM] Failed to signal cluster service event, error %1!u!.\n",
Status = GetLastError());
goto init_failure;
}
CloseHandle(RmpInitEvent);
//
// ResmonStop is initialized to TRUE by ClusSvc, we will wait for
// ClusSvc to signal when it is done attaching a debugger by waiting
// until ResmonStop is set to FALSE.
//
while ( RmpSharedState->ResmonStop ) {
Sleep(100);
}
//
// Boost our priority. Non-fatal if this fails.
//
if ( !SetPriorityClass( GetCurrentProcess(),
HIGH_PRIORITY_CLASS ) ) {
ClRtlLogPrint( LOG_UNUSUAL, "[RM] Failed to set priority class, error %1!u!.\n",
GetLastError() );
}
//
// Wait for shutdown. Either the cluster service terminating or
// the poller thread terminating will initiate a shutdown.
//
RmpWaitArray[0] = RmpRewaitEvent;
RmpWaitArray[1] = RmpClusterProcess;
//
// If we are notified a new thread is added, then just re-wait.
// N.B. RmpNumberOfThreads is really the number of threads, plus the
// two fixed wait events (the change notification and the Cluster Service).
//
do {
Status = WaitForMultipleObjects(RmpNumberOfThreads,
RmpWaitArray,
FALSE,
INFINITE);
} while ( (Status == WAIT_OBJECT_0) && (RmpShutdown == FALSE) );
ClRtlLogPrint( LOG_UNUSUAL, "[RM] Going away, Status = %1!u!, Shutdown = %2!u!.\n",
Status, RmpShutdown);
//
// Mark a flag if the cluster service process crashed.
//
if ( Status == ( WAIT_OBJECT_0 + 1 ) ) g_fRmpClusterProcessCrashed = TRUE;
RmpShutdown = TRUE;
CloseHandle( RmpRewaitEvent );
//
// Initiate RM shutdown.
//
s_RmShutdownProcess(NULL);
//
// Post shutdown of notification thread.
//
ClRtlLogPrint(LOG_NOISE, "[RM] Posting shutdown notification.\n");
RmpPostNotify( NULL, NotifyShutdown );
//
// Notify resources of a rundown if necessary. This must be done prior to flushing RPC
// calls since some resources (such as MNS) depend on this control code to get out of an
// entry point such as arbitrate which is invoked as an RPC by the cluster service.
//
RmpNotifyResourcesRundown();
//
// Shutdown RPC Server
//
Status = RpcMgmtStopServerListening ( NULL );
if ( Status == RPC_S_OK )
{
//
// Unregister the interface
//
Status = RpcServerUnregisterIf ( NULL, NULL, TRUE );
//
// Wait for all outstanding RPCs to complete
//
if ( Status == RPC_S_OK )
{
Status = RpcMgmtWaitServerListen ();
if ( Status != RPC_S_OK )
{
ClRtlLogPrint(LOG_UNUSUAL, "[RM] RpcMgmtWaitServerListen returns %1!u!\n", Status);
}
} else
{
ClRtlLogPrint(LOG_UNUSUAL, "[RM] RpcServerUnregisterIf returns %1!u!\n", Status);
}
} else
{
ClRtlLogPrint(LOG_UNUSUAL, "[RM] RpcMgmtStopServerListening returns %1!u!\n", Status);
}
//
// Clean up any resources left lying around by the cluster service. This must be done only after all outstanding RPCs have
// completed.
//
RmpRundownResources();
//
// Unitialize COM runtime only after all resources have been cleaned up, else they could AV if they are trying to use
// COM.
//
CoUninitialize () ;
return(0);
init_failure:
if ( RmpInitEvent != NULL ) {
CloseHandle( RmpInitEvent );
}
if ( RmpFileMapping != NULL ) {
CloseHandle( RmpFileMapping );
}
if ( RmpClusterProcess != NULL ) {
CloseHandle( RmpClusterProcess );
}
if ( RmpResTypesKey != NULL ) {
ClusterRegCloseKey( RmpResTypesKey );
}
if ( RmpResourcesKey != NULL ) {
ClusterRegCloseKey( RmpResourcesKey );
}
if ( RmpHCluster != NULL ) {
CloseCluster( RmpHCluster );
}
if ( RmpRewaitEvent != NULL ) {
CloseHandle( RmpRewaitEvent );
}
if ( comInited )
CoUninitialize();
if ( Inited )
CL_LOGFAILURE(Status);
return(Status);
} // main
VOID
RmpParseArgs(
int argc,
wchar_t *argv[],
OUT LPDWORD pClussvcProcessId,
OUT HANDLE* pClussvcFileMapping,
OUT HANDLE* pClussvcInitEvent,
OUT LPWSTR* pDebuggerCommand
)
/*++
Routine Description:
Parses the command line passed to the resource monitor
Required options:
-e EVENT supplies Event handle to be signalled when
initialization is complete
-m FILEMAPPING supplies file mapping handle to be
used for shared monitor state.
-p PROCESSID supplies process id of the cluster
service so resmon can detect failure of the
cluster service and shutdown cleanly.
-d [DEBUGGERCMD] - wait for or attach a debugger during startup
Additional options:
none
Arguments:
argc - supplies number of arguments
argv - supplies actual arguments
Return Value:
None.
--*/
{
int i;
wchar_t *p;
for (i=1; i<argc; i++) {
p=argv[i];
if ((*p == '-') ||
(*p == '/')) {
++p;
switch (toupper(*p)) {
case 'E':
if (i+1 < argc) {
*pClussvcInitEvent = LongToHandle(_wtoi(argv[++i]));
} else {
goto BadCommandLine;
}
break;
case 'M':
if (i+1 < argc) {
*pClussvcFileMapping = LongToHandle(_wtoi(argv[++i]));
} else {
goto BadCommandLine;
}
break;
case 'P':
if (i+1 < argc) {
*pClussvcProcessId = (DWORD)_wtoi(argv[++i]);
} else {
goto BadCommandLine;
}
break;
case 'D':
//
// use the empty (but not NULL) string to indicate that
// resmon should wait for a debugger to be attached.
//
if (i+1 < argc) {
if ( *argv[i+1] != UNICODE_NULL && *argv[i+1] != L'-' ) {
*pDebuggerCommand = argv[++i];
} else {
*pDebuggerCommand = L"";
}
} else {
*pDebuggerCommand = L"";
}
break;
default:
goto BadCommandLine;
}
}
}
return;
BadCommandLine:
ClusterLogEvent0(LOG_CRITICAL,
LOG_CURRENT_MODULE,
__FILE__,
__LINE__,
RMON_INVALID_COMMAND_LINE,
0,
NULL);
ExitProcess(0);
} // RmpParseArgs
DWORD
RmpInitialize(
VOID
)
/*++
Routine Description:
Initialize all resources needed by the resource monitor.
Arguments:
None.
Returns:
ERROR_SUCCESS if successful.
Win32 error code on failure.
--*/
{
//
// Initialize global data
//
InitializeCriticalSection(&RmpListLock);
InitializeCriticalSection(&RmpMonitorStateLock);
InitializeListHead(&RmpEventListHead);
ClRtlInitializeQueue(&RmpNotifyQueue);
return(ERROR_SUCCESS);
} // RmpInitialize
DWORD RmpLoadResType(
IN LPCWSTR lpszResourceTypeName,
IN LPCWSTR lpszDllName,
OUT PRESDLL_FNINFO pResDllFnInfo,
#ifdef COMRES
OUT PRESDLL_INTERFACES pResDllInterfaces,
#endif
OUT LPDWORD pdwCharacteristics
)
{
DWORD retry;
DWORD dwStatus = ERROR_SUCCESS;
HINSTANCE hDll = NULL;
PSTARTUP_ROUTINE pfnStartup;
PCLRES_FUNCTION_TABLE pFnTable = NULL;
LPWSTR pszDllName = (LPWSTR) lpszDllName;
PRM_DUE_TIME_ENTRY pDueTimeEntry = NULL;
pResDllFnInfo->hDll = NULL;
pResDllFnInfo->pResFnTable = NULL;
#ifdef COMRES
pResDllInterfaces->pClusterResource = NULL;
pResDllInterfaces->pClusterQuorumResource = NULL;
pResDllInterfaces->pClusterResControl = NULL;
#endif
// Expand any environment variables included in the DLL path name.
if ( wcschr( lpszDllName, L'%' ) != NULL ) {
pszDllName = ClRtlExpandEnvironmentStrings( lpszDllName );
if ( pszDllName == NULL ) {
dwStatus = GetLastError();
ClRtlLogPrint( LOG_UNUSUAL, "[RM] ResTypeControl: Error expanding environment strings in '%1!ls!, error %2!u!.\n",
lpszDllName,
dwStatus);
goto FnExit;
}
}
// Load the dll... we can't assume we have the DLL loaded!
hDll = LoadLibraryW(pszDllName);
if ( hDll == NULL )
{
dwStatus = GetLastError();
ClRtlLogPrint( LOG_CRITICAL, "[RM] ResTypeControl: Error loading resource DLL '%1!ls!', error %2!u!.\n",
pszDllName,
dwStatus);
#ifdef COMRES
dwStatus = RmpLoadComResType(lpszDllName, pResDllInterfaces,
pdwCharacteristics);
//
// Map COM errors to a specific Win32 error code that the cluster service relies on
// for figuring out whether this node supports this restype.
//
if ( dwStatus != S_OK )
{
ClRtlLogPrint(LOG_CRITICAL, "[RM] ResTypeControl: Error loading resource DLL '%1!ls!', COM error 0x%2!08lx!...\n",
pszDllName,
dwStatus);
dwStatus = ERROR_MOD_NOT_FOUND;
}
#endif
goto FnExit;
}
//
// Invoke debugger if one is specified.
//
if ( RmpDebugger ) {
//
// Wait for debugger to come online.
//
retry = 100;
while ( retry-- &&
!IsDebuggerPresent() ) {
Sleep(100);
}
OutputDebugStringA("[RM] ResourceTypeControl: Just loaded resource DLL ");
OutputDebugStringW(lpszDllName);
OutputDebugStringA("\n");
DebugBreak();
}
// Get the startup routine
pfnStartup = (PSTARTUP_ROUTINE)GetProcAddress( hDll,
STARTUP_ROUTINE );
if ( pfnStartup == NULL ) {
dwStatus = GetLastError();
ClRtlLogPrint(LOG_CRITICAL, "[RM] ResTypeControl: Error getting startup routine of %1!ws!, status %2!u!.\n",
lpszDllName,
dwStatus);
goto FnExit;
}
// Get the function table
RmpSetMonitorState(RmonStartingResource, NULL);
//
// Insert the DLL & entry point info into the deadlock monitoring list. Make sure
// you remove the entry after you finish the entry point call, else you will kill
// this process on a FALSE deadlock positive.
//
pDueTimeEntry = RmpInsertDeadlockMonitorList ( lpszDllName,
lpszResourceTypeName,
NULL,
L"Startup" );
try {
dwStatus = (pfnStartup)( lpszResourceTypeName,
CLRES_VERSION_V1_00,
CLRES_VERSION_V1_00,
RmpSetResourceStatus,
RmpLogEvent,
&pFnTable );
} except (EXCEPTION_EXECUTE_HANDLER) {
dwStatus = GetExceptionCode();
}
RmpRemoveDeadlockMonitorList( pDueTimeEntry );
RmpSetMonitorState(RmonIdle, NULL);
if ( dwStatus != ERROR_SUCCESS ) {
ClRtlLogPrint(LOG_CRITICAL, "[RM] ResTypeControl: Startup call for %1!ws! failed, error %2!u!.\n",
lpszDllName,
dwStatus);
goto FnExit;
}
if ( pFnTable == NULL ) {
ClRtlLogPrint(LOG_CRITICAL, "[RM] ResTypeControl: Startup function table is NULL for %1!ws!\n", lpszDllName);
dwStatus = ERROR_INVALID_DATA;
goto FnExit;
}
if ( pFnTable->Version != CLRES_VERSION_V1_00 ) {
ClRtlLogPrint(LOG_CRITICAL, "[RM] ResTypeControl: Incorrect function table version for %1!ws!\n", lpszDllName);
dwStatus = ERROR_INVALID_DATA;
goto FnExit;
}
if ( pFnTable->TableSize != CLRES_V1_FUNCTION_SIZE ) {
ClRtlLogPrint(LOG_CRITICAL, "[RM] ResTypeControl: Incorrect function table size for %1!ws!\n", lpszDllName);
dwStatus = ERROR_INVALID_DATA;
goto FnExit;
}
if ( (pFnTable->V1Functions.Arbitrate != NULL) &&
(pFnTable->V1Functions.Release != NULL) && pdwCharacteristics) {
*pdwCharacteristics = CLUS_CHAR_QUORUM;
}
FnExit:
if (dwStatus != ERROR_SUCCESS)
{
if (hDll) FreeLibrary(hDll);
if (pFnTable) LocalFree(pFnTable);
}
else
{
pResDllFnInfo->hDll = hDll;
pResDllFnInfo->pResFnTable = pFnTable;
}
if ( pszDllName != lpszDllName )
{
LocalFree( pszDllName );
}
return(dwStatus);
} //*** RmpLoadResType()
#ifdef COMRES
DWORD RmpLoadComResType(
IN LPCWSTR lpszDllName,
OUT PRESDLL_INTERFACES pResDllInterfaces,
OUT LPDWORD pdwCharacteristics)
{
IClusterResource *pClusterResource = NULL ;
IClusterQuorumResource *pClusterQuorumResource = NULL;
IClusterResControl *pClusterResControl = NULL;
HRESULT hr ;
CLSID clsid ;
DWORD Error ;
pResDllInterfaces->pClusterResource = NULL;
pResDllInterfaces->pClusterQuorumResource = NULL;
pResDllInterfaces->pClusterResControl = NULL;
hr = CLSIDFromProgID(lpszDllName, &clsid) ;
if (FAILED (hr))
{
ClRtlLogPrint( LOG_UNUSUAL, "[RM] Error converting CLSIDFromProgID Prog ID %1!ws!, error %2!u!.\n",
lpszDllName, hr);
goto FnExit ;
}
if ((hr = CoCreateInstance (&clsid, NULL, CLSCTX_ALL, &IID_IClusterResource, (LPVOID *) &pClusterResource)) != S_OK)
goto FnExit ;
//not a mandatory interface
hr = IClusterResource_QueryInterface (pClusterResource, &IID_IClusterQuorumResource, (LPVOID *) &pClusterQuorumResource) ;
if (SUCCEEDED(hr))
{
if (pdwCharacteristics)
*pdwCharacteristics = CLUS_CHAR_QUORUM;
IClusterQuorumResource_Release (pClusterQuorumResource) ;
}
//not a mandatory interface
hr = IClusterResource_QueryInterface (
pClusterResource,
&IID_IClusterResControl,
(LPVOID *) &pClusterResControl
) ;
if (SUCCEEDED(hr))
{
*pdwCharacteristics = CLUS_CHAR_QUORUM;
IClusterQuorumResource_Release (pClusterResControl) ;
}
hr = S_OK;
FnExit:
if (hr != S_OK)
{
if (pClusterResource)
IClusterResource_Release (pClusterResource) ;
if (pClusterQuorumResource)
IClusterQuorumResource_Release (pClusterQuorumResource) ;
if (pClusterResControl)
IClusterResControl_Release (pClusterResControl) ;
}
else
{
pResDllInterfaces->pClusterResource = pClusterResource;
pResDllInterfaces->pClusterQuorumResource = pClusterQuorumResource;
pResDllInterfaces->pClusterResControl = pClusterResControl;
}
return(hr);
}
#endif //end of #ifdef COMRES
RPC_STATUS
RmpGetDynamicEndpointName(
OUT LPWSTR *ppResmonRpcDynamicEndpointName
)
/*++
Routine Description:
Get the name of the dynamic endpoint the resource monitor registered.
Arguments:
ppResmonRpcDynamicEndpointName - The dynamic endpoint name string.
Returns:
RPC_S_OK on success. An RPC error code otherwise
--*/
{
RPC_STATUS rpcStatus;
RPC_BINDING_VECTOR *pServerBindingVector = NULL;
DWORD i;
WCHAR *pszProtSeq = NULL, *pServerStringBinding = NULL;
//
// Get the server binding vector. This includes all the protocols and EP's registered
// so far.
//
rpcStatus = RpcServerInqBindings( &pServerBindingVector );
if ( rpcStatus != RPC_S_OK )
{
ClRtlLogPrint(LOG_CRITICAL,
"[RM] RmpGetDynamicEndpointName: Unable to inquire server bindings, status %1!u!.\n",
rpcStatus);
goto FnExit;
}
//
// Grovel the binding vector looking for the LRPC protocol information.
//
for( i = 0; i < pServerBindingVector->Count; i++ )
{
rpcStatus = RpcBindingToStringBindingW( pServerBindingVector->BindingH[i],
&pServerStringBinding );
if ( rpcStatus != RPC_S_OK )
{
ClRtlLogPrint(LOG_CRITICAL,
"[RM] RmpGetDynamicEndpointName: Unable to convert binding to string, status %1!u!.\n",
rpcStatus);
goto FnExit;
}
rpcStatus = RpcStringBindingParseW( pServerStringBinding,
NULL,
&pszProtSeq,
NULL,
ppResmonRpcDynamicEndpointName,
NULL );
if ( rpcStatus != RPC_S_OK )
{
ClRtlLogPrint(LOG_CRITICAL,
"[RM] RmpGetDynamicEndpointName: Unable to parse server string binding, status %1!u!.\n",
rpcStatus);
goto FnExit;
}
//
// If you found the LRPC protocol info, then you must have the endpoint info. Return
// with success.
//
if ( lstrcmpW ( pszProtSeq, L"ncalrpc" ) == 0 )
{
ClRtlLogPrint(LOG_NOISE,
"[RM] RmpGetDynamicEndpointName: Successfully got LRPC endpoint info, EP name is %1!ws!\n",
*ppResmonRpcDynamicEndpointName);
goto FnExit;
}
RpcStringFreeW ( &pszProtSeq );
pszProtSeq = NULL;
RpcStringFreeW ( ppResmonRpcDynamicEndpointName );
*ppResmonRpcDynamicEndpointName = NULL;
RpcStringFreeW ( &pServerStringBinding );
pServerStringBinding = NULL;
} // for
//
// If you didn't find the LRPC information, return an error.
//
if ( i == pServerBindingVector->Count )
{
rpcStatus = RPC_S_NO_BINDINGS;
ClRtlLogPrint(LOG_CRITICAL,
"[INIT] RmpGetDynamicEndpointName: Unable to get info on the LRPC binding, status %1!u!.\n",
rpcStatus);
goto FnExit;
}
FnExit:
//
// Free the strings and the binding vector if they haven't already been freed
//
if ( pszProtSeq != NULL ) RpcStringFreeW ( &pszProtSeq );
if ( pServerStringBinding != NULL ) RpcStringFreeW ( &pServerStringBinding );
if ( pServerBindingVector != NULL ) RpcBindingVectorFree ( &pServerBindingVector );
return ( rpcStatus );
} // RmpCleanup
DWORD
RmpSaveDynamicEndpointName(
IN LPWSTR pResmonRpcDynamicEndpointName
)
/*++
Routine Description:
Save the name of the dynamic endpoint the resource monitor registered to the cluster service parameters key.
Arguments:
pResmonRpcDynamicEndpointName - The dynamic endpoint name string.
Returns:
ERROR_SUCCESS on success. A Win32 error code otherwise.
Comments:
This function saves the registered resource monitor dynamic EP text string to the registry and the cluster service reads the
registry to know the EP value. This is similar to what SCM does with its services. By using the registry as opposed to
using the shared memory mapped section between cluster service and the resource monitor, we are not limited by
any string sizes for the EP value and so this approach is safer compared to the shared memory way.
--*/
{
HKEY hParamsKey = NULL;
DWORD dwStatus;
//
// Open key to SYSTEM\CurrentControlSet\Services\ClusSvc\Parameters
//
dwStatus = RegOpenKeyW( HKEY_LOCAL_MACHINE,
CLUSREG_KEYNAME_CLUSSVC_PARAMETERS,
&hParamsKey );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL, "[RM] Error in opening cluster service params key, status %1!u!\n",
dwStatus);
goto FnExit;
}
//
// Set the dynamic endpoint name
//
dwStatus = RegSetValueExW( hParamsKey,
CLUSREG_NAME_SVC_PARAM_RESMON_EP,
0, // reserved
REG_SZ,
( LPBYTE ) pResmonRpcDynamicEndpointName,
( lstrlenW ( pResmonRpcDynamicEndpointName ) + 1 ) *
sizeof ( WCHAR ) );
if ( dwStatus != ERROR_SUCCESS )
{
ClRtlLogPrint(LOG_CRITICAL, "[RM] Error in setting endpoint name, status %1!u!\n",
dwStatus);
goto FnExit;
}
FnExit:
if ( hParamsKey ) RegCloseKey ( hParamsKey );
return ( dwStatus );
}// RmpSaveDynamicEndpointName
DWORD IsUserAdmin(
BOOL * pbIsAdmin )
/*++
Routine Description:
Determines whether the current user session has administrative privileges. Must
impersonate the client before calling this function.
Arguments:
pbIsAdmin -- set to TRUE if the current user has admin privileges.
Returns:
ERROR_SUCCESS if successful.
Win32 error code on failure.
--*/
{
SID_IDENTIFIER_AUTHORITY sidNtAuth = SECURITY_NT_AUTHORITY;
PSID psidAdminGroup;
DWORD dwStatus = ERROR_SUCCESS;
if ( !AllocateAndInitializeSid(
&sidNtAuth ,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&psidAdminGroup))
{
dwStatus = GetLastError();
}
else
{
*pbIsAdmin = FALSE;
if (!CheckTokenMembership( NULL, psidAdminGroup, pbIsAdmin ))
{
dwStatus = GetLastError();
}
FreeSid(psidAdminGroup);
}
return dwStatus;
}
RPC_STATUS
ResmonRpcConnectCallback(
IN RPC_IF_ID * Interface,
IN void * Context
)
/*++
Routine Description:
RPC callback for authenticating connecting clients of resmon.
Arguments:
Interface (unused) - Supplies the UUID and version of the interface.
Context - Supplies a server binding handle representing the client
Return Value:
RPC_S_OK if the user is granted permission.
RPC_S_ACCESS_DENIED if the user is denied permission.
Win32 error code otherwise.
--*/
{
RPC_STATUS RpcStatus = RPC_S_OK;
DWORD dwStatus;
RPC_AUTHZ_HANDLE hPrivs;
DWORD dwAuthnLevel;
BOOL bIsAdmin = FALSE;
//
// Verify the authentication level of the client.
//
RpcStatus = RpcBindingInqAuthClient( Context,
&hPrivs,
NULL,
&dwAuthnLevel,
NULL,
NULL );
if ( RpcStatus != RPC_S_OK )
{
goto FnExit;
}
if ( dwAuthnLevel < RPC_C_AUTHN_LEVEL_PKT_PRIVACY )
{
RpcStatus = RPC_S_ACCESS_DENIED;
goto FnExit;
}
// Impersonate the client so we can call IsUserAdmin.
if ( ( RpcStatus = RpcImpersonateClient( Context ) ) != RPC_S_OK )
{
dwStatus = I_RpcMapWin32Status(RpcStatus);
goto FnExit;
}
// Check that the caller's account is local system account
dwStatus = IsUserAdmin( &bIsAdmin );
RpcRevertToSelf();
if (dwStatus != ERROR_SUCCESS )
{
RpcStatus = RPC_E_ACCESS_DENIED;
goto FnExit;
}
if ( !bIsAdmin )
{
RpcStatus = RPC_E_ACCESS_DENIED;
goto FnExit;
}
FnExit:
return RpcStatus;
}