|
|
/*++
Copyright (c) 1995-1997 Microsoft Corporation
Module Name:
clusrtl.c
Abstract:
Provides run-time library support common to any module of the NT Cluster.
Author:
John Vert (jvert) 1-Dec-1995
Revision History:
--*/ #include "clusrtlp.h"
#include "stdarg.h"
#include "stdlib.h"
#include "clusverp.h"
#include "windns.h"
#include "security.h"
#include "secext.h"
#define WMI_TRACING 1
#define RPC_WMI_TRACING 1
#if defined(WMI_TRACING)
// 789aa2d3-e298-4d8b-a3a3-a8a0ec9c7702 -- Rpc
// b1599392-1a0f-11d3-ba86-00c04f8eed00 -- ClusSvc
#define WPP_CONTROL_GUIDS \
WPP_DEFINE_CONTROL_GUID(ClusRtl,(b1599392,1a0f,11d3,ba86,00c04f8eed00), \ WPP_DEFINE_BIT(Error) \ WPP_DEFINE_BIT(Unusual) \ WPP_DEFINE_BIT(Noise) \ ) \ WPP_DEFINE_CONTROL_GUID(ClusRpc,(789aa2d3,e298,4d8b,a3a3,a8a0ec9c7702), \ WPP_DEFINE_BIT(RpcTrace) \ ) //#define WppDebug(x,y) ClRtlPrintf y
#include "clusrtl.tmh"
#define REG_TRACE_CLUSTERING L"Clustering Service"
#endif // defined(WMI_TRACING)
//
// Local Macros
//
//
// SC Manager failure action parameters. set STARTUP_FAILURE_RESTART to one
// before shipping to get the normal backoff behavior.
//
#if STARTUP_FAILURE_RESTART
#define CLUSTER_FAILURE_RETRY_COUNT -1 // forever
#else
#define CLUSTER_FAILURE_RETRY_COUNT 0
#endif
#define CLUSTER_FAILURE_MAX_STARTUP_RETRIES 30
#define CLUSTER_FAILURE_INITIAL_RETRY_INTERVAL 60 * 1000 // 60 secs
#define CLUSTER_FAILURE_FINAL_RETRY_INTERVAL ( 60 * 1000 * 16) // 16 mins
#define ClRtlAcquirePrintLock() \
WaitForSingleObject( ClRtlPrintFileMutex, INFINITE );
#define ClRtlReleasePrintLock() \
ReleaseMutex( ClRtlPrintFileMutex );
#define LOGFILE_NAME L"Cluster.log"
#define LOGENTRY_BUFFER_SIZE 512
//
// Private Data
//
BOOL ClRtlpDbgOutputToConsole = FALSE; BOOL ClRtlpInitialized = FALSE; BOOL ClRtlPrintToFile = FALSE; HANDLE ClRtlPrintFile = NULL; HANDLE ClRtlPrintFileMutex = NULL; DWORD ClRtlProcessId; PDWORD ClRtlDbgLogLevel; HANDLE ClRtlWatchdogTimerQueue = NULL;
#define MAX_NUMBER_LENGTH 20
// Specify maximum file size ( DWORD / 1MB )
#define MAX_FILE_SIZE ( 0xFFFFF000 / ( 1024 * 1024 ) )
DWORD ClRtlPrintFileLimit = ( 8 * 1024 * 1024 ); // 8 MB default
DWORD ClRtlPrintFileLoWater = 0;
//
// Public Routines
//
// !!!!NOTE!!!!
// This initialization routine is call from DllMain(), do not add anyting out here that requires synchronization. Do not add any win32 api calls here.
//
DWORD ClRtlInitialize( IN BOOL DbgOutputToConsole, IN PDWORD DbgLogLevel ) { WCHAR logFileBuffer[MAX_PATH]; LPWSTR logFileName = NULL; DWORD Status = ERROR_SUCCESS; DWORD defaultLogSize = 8; HKEY ClusterKey; WCHAR modulePath[MAX_PATH]; DWORD envLength; WCHAR logFileSize[MAX_NUMBER_LENGTH]; DWORD logSize; UNICODE_STRING logFileString; LPWSTR lpszBakFileName = NULL; DWORD fileSizeHigh = 0; DWORD fileSizeLow;
//
// init event stuff so we have a means for logging other failures
//
ClRtlEventLogInit();
if (!ClRtlpInitialized) { ClRtlpDbgOutputToConsole = DbgOutputToConsole; ClRtlpInitialized = TRUE; ClRtlDbgLogLevel = DbgLogLevel;
envLength = GetEnvironmentVariable(L"ClusterLog", logFileBuffer, sizeof(logFileBuffer)/sizeof(WCHAR)); if ( envLength > sizeof(logFileBuffer)/sizeof(WCHAR) ) { logFileName = LocalAlloc( LMEM_FIXED, envLength * sizeof( WCHAR ) ); if ( logFileName == NULL ) { return GetLastError(); }
envLength = GetEnvironmentVariable(L"ClusterLog", logFileName, envLength); if ( envLength == 0 ) { LocalFree( logFileName ); logFileName = NULL; } } else if ( envLength != 0 ) { logFileName = logFileBuffer; }
#if CLUSTER_BETA
//
// always turn on logging when in beta mode
//
if ( ( logFileName != NULL ) && ( *logFileName == UNICODE_NULL ) ) { WCHAR *p;
if ( GetModuleFileName(NULL, modulePath, MAX_PATH - sizeof(LOGFILE_NAME)/sizeof(WCHAR) ) ) { p = wcsrchr( modulePath, '\\' ); if ( p != UNICODE_NULL ) { p++; *p = UNICODE_NULL; wcscat( modulePath, LOGFILE_NAME ); logFileName = modulePath; } } } #endif
if ( logFileName != NULL ) { //
// Try to get a limit on the log file size.
// This number is the number of MB.
//
envLength = GetEnvironmentVariable(L"ClusterLogSize", logFileSize, sizeof(logFileSize)/sizeof(WCHAR)); if ( (envLength != 0) && (envLength < MAX_NUMBER_LENGTH) ) { RtlInitUnicodeString( &logFileString, logFileSize ); Status = RtlUnicodeStringToInteger( &logFileString, 10, &logSize ); if ( NT_SUCCESS( Status ) ) { ClRtlPrintFileLimit = logSize; } } else { ClRtlPrintFileLimit = defaultLogSize; }
Status = ERROR_SUCCESS;
if ( ClRtlPrintFileLimit == 0 ) { goto exit; }
if ( ClRtlPrintFileLimit > MAX_FILE_SIZE ) { ClRtlPrintFileLimit = MAX_FILE_SIZE; } ClRtlPrintFileLimit = ClRtlPrintFileLimit * ( 1024 * 1024 );
ClRtlPrintFileMutex = CreateMutex( NULL, FALSE, L"ClusterRtlPrintFileMutex" ); if ( ClRtlPrintFileMutex != NULL ) { BOOL createdDirectory = FALSE; //
// Chittur Subbaraman (chitturs) - 11/11/98
//
// Check whether the ClusterLogOverwrite environment var is
// defined.
//
envLength = GetEnvironmentVariable( L"ClusterLogOverwrite", NULL, 0 ); if ( envLength != 0 ) { HANDLE hLogFile = INVALID_HANDLE_VALUE; //
// Check whether someone else has an open handle to
// the log file. If so, don't attempt anything.
//
hLogFile = CreateFile( logFileName, GENERIC_READ | GENERIC_WRITE, 0, // Exclusive file share mode
NULL, OPEN_EXISTING, 0, NULL ); if ( hLogFile != INVALID_HANDLE_VALUE ) { CloseHandle( hLogFile );
lpszBakFileName = LocalAlloc( LMEM_FIXED, ( 5 + lstrlenW( logFileName ) ) * sizeof( WCHAR ) ); if ( lpszBakFileName == NULL ) { Status = GetLastError(); ClRtlDbgPrint(LOG_CRITICAL, "[ClRtl] Mem alloc for .bak file name failed. Error %1!u!\n", Status); goto exit; }
//
// Append ".bak" to the log file name
//
lstrcpyW( lpszBakFileName, logFileName ); lstrcatW( lpszBakFileName, L".bak" );
//
// Copy the log file (if it exists) to a bak file
// and then delete the log file
//
if ( CopyFileW( logFileName, lpszBakFileName, FALSE ) ) { if ( !DeleteFileW( logFileName ) ) { //
// There is no reason for this to happen since the
// log file should be deletable.
//
Status = GetLastError(); ClRtlDbgPrint(LOG_CRITICAL, "[ClRtl] Error %1!u! in deleting cluster log file\n", Status); goto exit; } } } }
openFileRetry: ClRtlPrintFile = CreateFile(logFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL );
if ( ClRtlPrintFile == INVALID_HANDLE_VALUE ) { Status = GetLastError();
if ( !createdDirectory && Status == ERROR_PATH_NOT_FOUND ) { PWCHAR lastSlash = wcsrchr( logFileName, '\\' ); WCHAR slashChar;
if ( lastSlash == NULL ) { lastSlash = wcsrchr( logFileName, '/' ); }
if ( lastSlash != NULL ) { slashChar = *lastSlash; *lastSlash = UNICODE_NULL; Status = ClRtlCreateDirectory( logFileName );
if ( Status == ERROR_SUCCESS ) { createdDirectory = TRUE; *lastSlash = slashChar; goto openFileRetry; } } }
ClRtlDbgPrint(LOG_CRITICAL, "[ClRtl] Open of log file failed. Error %1!u!\n", Status); goto exit; } else { ClRtlPrintToFile = TRUE; ClRtlProcessId = GetCurrentProcessId();
//
// determine the initial low water mark. We have 3 cases
// we need to handle:
// 1) log size is less than 1/2 limit
// 2) log size is within limit but more than 1/2 limit
// 3) log size is greater than limit
//
// case 1 requires nothing special; the low water mark
// will be updated on the next log write.
//
// for case 2, we need to find the beginning of a line
// near 1/2 the current limit. for case 3, the place to
// start looking is current log size - 1/2 limit. In this
// case, the log will be truncated before the first write
// occurs, so we need to take the last 1/2 limit bytes and
// copy them down to the front.
//
//
ClRtlAcquirePrintLock(); fileSizeLow = GetFileSize( ClRtlPrintFile, &fileSizeHigh ); if ( fileSizeLow < ( ClRtlPrintFileLimit / 2 )) { //
// case 1: leave low water at zero; it will be updated
// with next log write
//
; } else { #define LOGBUF_SIZE 1024
CHAR buffer[LOGBUF_SIZE]; LONG currentPosition; DWORD bytesRead;
if ( fileSizeLow < ClRtlPrintFileLimit ) { //
// case 2; start looking at the 1/2 the current
// limit to find the starting position
//
currentPosition = ClRtlPrintFileLimit / 2; } else { //
// case 3: start at current size minus 1/2 limit
// to find our starting position.
//
currentPosition = fileSizeLow - ( ClRtlPrintFileLimit / 2 ); }
//
// read in a block (backwards) from the initial file
// position and look for a newline char. When we find
// one, the next char is the first char on a new log
// line. use that as the initial starting position
// when we finally truncate the file.
//
ClRtlPrintFileLoWater = 0; currentPosition -= LOGBUF_SIZE;
SetFilePointer(ClRtlPrintFile, currentPosition, &fileSizeHigh, FILE_BEGIN); if ( ReadFile(ClRtlPrintFile, buffer, LOGBUF_SIZE, &bytesRead, NULL ) ) { PCHAR p = &buffer[ bytesRead - 1 ];
while ( *p != '\n' && bytesRead-- != 0 ) { --p; } if ( *p == '\n' ) { ClRtlPrintFileLoWater = (DWORD)(currentPosition + ( p - buffer + 1 )); } }
if ( ClRtlPrintFileLoWater == 0 ) { //
// couldn't find any reasonable data. just set it to
// initial current position.
//
ClRtlPrintFileLoWater = currentPosition + LOGBUF_SIZE; } } ClRtlReleasePrintLock(); } } else { Status = GetLastError(); ClRtlDbgPrint(LOG_UNUSUAL, "[ClRtl] Unable to create print file mutex. Error %1!u!.\n", Status); Status = ERROR_SUCCESS; //goto exit;
} } }
exit: if ( logFileName != logFileBuffer && logFileName != modulePath ) { LocalFree( logFileName ); }
//
// Chittur Subbaraman (chitturs) - 11/11/98
//
if ( lpszBakFileName != NULL ) { LocalFree( lpszBakFileName ); }
return Status;
} // ClRtlInitialize
#ifdef RPC_WMI_TRACING
typedef DWORD (*I_RpcEnableWmiTraceFunc )( VOID* fn, // Rpc now uses TraceMessage, no need to pass trace func
WPP_WIN2K_CONTROL_BLOCK ** pHandle );
HINSTANCE hInstRpcrt4;
#endif
DWORD ClRtlIsServicesForMacintoshInstalled( OUT BOOL * pfInstalled )
/*++
Routine Description:
Determines if SFM is installed on the local system.
Arguments:
pfInstalled - pointer to a boolean flag to return whether SFM is installed returns: TRUE if SFM is installed FALSE if SFM is not installed
Return Value:
Status of request. ERROR_SUCCESS if valid info in pfInstalled. Error Code otherwise. On error pfInstalled (if present) is set to FALSE
--*/
{ HANDLE scHandle; HANDLE scServiceHandle;
if ( ARGUMENT_PRESENT( pfInstalled ) ) { *pfInstalled = FALSE; } else { return ERROR_INVALID_PARAMETER; } scHandle = OpenSCManager( NULL, // Open on local machine
NULL, // Open SERVICES_ACTIVE_DATABASE
GENERIC_READ ); if ( scHandle == NULL ) { return( GetLastError() ); }
scServiceHandle = OpenService( scHandle, L"macfile", READ_CONTROL ); if ( scServiceHandle != NULL ) { *pfInstalled = TRUE; } CloseServiceHandle( scServiceHandle ); CloseServiceHandle( scHandle );
return ERROR_SUCCESS;
} // ClRtlIsServicesForMacintoshInstalled
DWORD ClRtlInitWmi( LPCWSTR ComponentName ) { #if defined(RPC_WMI_TRACING)
{ DWORD Status = ERROR_SUCCESS; PWPP_WIN2K_CONTROL_BLOCK RpcCb; I_RpcEnableWmiTraceFunc RpcEnableWmiTrace = 0;
hInstRpcrt4 = LoadLibrary(L"rpcrt4.dll"); if (hInstRpcrt4) { RpcEnableWmiTrace = (I_RpcEnableWmiTraceFunc) GetProcAddress(hInstRpcrt4, "I_RpcEnableWmiTrace");
if (RpcEnableWmiTrace) {
Status = (*RpcEnableWmiTrace)(0, &RpcCb); if (Status == ERROR_SUCCESS) { WPP_SET_FORWARD_PTR(RpcTrace, WPP_VER_WIN2K_CB_FORWARD_PTR, RpcCb); }
} else { ClRtlDbgPrint(LOG_UNUSUAL, "[ClRtl] rpcrt4.dll GetWmiTraceEntryPoint failed, status %1!d!.\n", GetLastError() ); } } } #endif // RPC_WMI_TRACING
WPP_INIT_TRACING(NULL); // Don't need publishing
WppAutoStart(ComponentName); return ERROR_SUCCESS; }
VOID ClRtlCleanup( VOID ) { if (ClRtlpInitialized) { ClRtlpInitialized = FALSE; ClRtlEventLogCleanup();
//Cleaning up watchdog stuff
if(ClRtlWatchdogTimerQueue != NULL) { DeleteTimerQueue(ClRtlWatchdogTimerQueue); ClRtlWatchdogTimerQueue = NULL; } WPP_CLEANUP(); #if defined(RPC_WMI_TRACING)
if (hInstRpcrt4) { FreeLibrary(hInstRpcrt4); } #endif
}
return; }
VOID ClRtlpWatchdogCallback( PVOID par, BOOLEAN timedOut ) {
PWATCHDOGPAR pPar=(PWATCHDOGPAR)par;
if(!timedOut) { // The timer was cancelled, get out.
ClRtlLogPrint(LOG_NOISE, "[ClRtl] Watchdog Timer Cancelled, ThreadId= 0x%1!x! par= %2!ws!.\n", pPar->threadId, pPar->par ); return; }
ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] Watchdog timer timed out, ThreadId= 0x%1!x! par= %2!ws!.\n", pPar->threadId, pPar->par );
#ifdef CLUSTER_BETA
// Breaking into NTSD if available or KD. Do it only for cluster beat builds.
DebugBreak(); #endif
}
PVOID ClRtlSetWatchdogTimer( DWORD timeout, LPWSTR par ) {
PWATCHDOGPAR pPar;
// Do the initialization here not in ClRtlInitialize()
if(ClRtlWatchdogTimerQueue == NULL) { if((ClRtlWatchdogTimerQueue = CreateTimerQueue()) == NULL) { return NULL; } }
if((pPar = LocalAlloc(LMEM_FIXED, sizeof(WATCHDOGPAR))) == NULL) { return NULL; } pPar->par = par; pPar->threadId = GetCurrentThreadId();
if(!CreateTimerQueueTimer( &pPar->wTimer, ClRtlWatchdogTimerQueue, ClRtlpWatchdogCallback, (PVOID)pPar, timeout, 0, 0)) { LocalFree(pPar); return NULL; }
ClRtlLogPrint(LOG_NOISE, "[ClRtl] Setting watchdog timer= 0x%1!x!, Timeout= %2!u!(ms), par= %3!ws!.\n", pPar->wTimer, timeout, par ); return (PVOID)pPar;
}
VOID ClRtlCancelWatchdogTimer( PVOID wTimer ) {
PWATCHDOGPAR pPar=(PWATCHDOGPAR)wTimer; if((ClRtlWatchdogTimerQueue == NULL) || (wTimer == NULL)) { return; }
if(!DeleteTimerQueueTimer( ClRtlWatchdogTimerQueue, pPar->wTimer, INVALID_HANDLE_VALUE )) { ClRtlLogPrint(LOG_CRITICAL, "[ClRtl] Failed to cancel watchdog timer 0x%1!x!.\n", pPar->wTimer ); } else { ClRtlLogPrint(LOG_NOISE, "[ClRtl] Cancelled watchdog timer 0x%1!x!.\n", pPar->wTimer ); } LocalFree(wTimer); }
BOOL ClRtlCheckForLogCorruption( LPSTR pszOutBuffer ) //
// Find the log corrupter. There should never be move than 4
// question marks in a row or character below 32 or above 128
// if English.
//
// Returns:
// TRUE if it is safe to write
// FALSE if it is NOT safe to write
//
{ DWORD count; WCHAR szLocale[ 32 ]; static BOOL fLocaleFound = FALSE; static BOOL fEnglish = FALSE;
if ( !pszOutBuffer ) return FALSE;
if ( !fLocaleFound ) { GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SENGLANGUAGE, szLocale, 32 );
if ( lstrcmpiW( szLocale, L"ENGLISH" ) == 0 ) { fEnglish = TRUE; }
fLocaleFound = TRUE; }
for( count = 0; *pszOutBuffer; pszOutBuffer++ ) { if ( *pszOutBuffer == '?' ) { count++; if ( count > 4 ) { return FALSE; } } else if ( fEnglish && ( ( *pszOutBuffer < 32 && *pszOutBuffer != 0x0A // linefeed
&& *pszOutBuffer != 0x0D // creturn
&& *pszOutBuffer != 0x09 ) // tab
|| *pszOutBuffer > 128 ) ) { return FALSE; } }
return TRUE;
} // ClRtlCheckForLogCorruption
__inline BOOL ClRtlpIsOutputDeviceAvailable( VOID )
/*++
Routine Description:
Description
Arguments:
None
Return Value:
None
--*/
{ //
// normally, there is nothing to do
//
return ( ClRtlpDbgOutputToConsole || IsDebuggerPresent()); } // ClRtlpIsOutputDeviceAvailable
VOID ClRtlpOutputString( IN PCHAR String )
/*++
Routine Description:
Outputs the specified string based on the current settings
Arguments:
String - Specifies the string to output.
Return Value:
None.
--*/
{ static PCRITICAL_SECTION dbgPrintLock = NULL; PCRITICAL_SECTION testPrintLock;
//
// synchronize threads by interlocking the assignment of the global lock.
//
if ( dbgPrintLock == NULL ) { testPrintLock = LocalAlloc( LMEM_FIXED, sizeof( CRITICAL_SECTION )); if ( testPrintLock == NULL ) { return; }
InitializeCriticalSection( testPrintLock ); InterlockedCompareExchangePointer( &dbgPrintLock, testPrintLock, NULL );
//
// only one thread did the exchange; the loser deallocates its
// allocation and switches over to using the real lock
//
if ( dbgPrintLock != testPrintLock ) { DeleteCriticalSection( testPrintLock ); LocalFree( testPrintLock ); } }
EnterCriticalSection( dbgPrintLock );
//
// print to console window has precedence. Besides, if console is the
// debugger window, you get double output
//
if (ClRtlpDbgOutputToConsole) { printf( String ); } else if ( IsDebuggerPresent()) { OutputDebugStringA(String); }
LeaveCriticalSection( dbgPrintLock );
} // ClRtlpOutputString
VOID ClRtlMsgPrint( IN DWORD MessageId, ... )
/*++
Routine Description:
Prints a message to the debugger or console, as appropriate
Does not alter the formatting of the message as it occurs in the message file.
Arguments:
MessageId - The message id of the string to print
Any FormatMessage compatible arguments to be inserted in the ErrorMessage before it is logged.
Return Value:
None.
--*/
{ CHAR szOutBuffer[LOGENTRY_BUFFER_SIZE]; DWORD Bytes; NTSTATUS Status; va_list ArgList;
//
// don't go any further if nothing to do
//
if ( !ClRtlpIsOutputDeviceAvailable()) { return; }
va_start(ArgList, MessageId);
try { Bytes = FormatMessageA(FORMAT_MESSAGE_FROM_HMODULE, NULL, MessageId, 0, szOutBuffer, sizeof(szOutBuffer) / sizeof(szOutBuffer[0]), &ArgList); } except ( EXCEPTION_EXECUTE_HANDLER ) { Bytes = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, L"LOGERROR(exception): Could not format message ID #%1!u!\n", 0, 0, szOutBuffer, sizeof(szOutBuffer) / sizeof(szOutBuffer[0]), (va_list *) &MessageId ); }
va_end(ArgList);
if (Bytes != 0) { if ( !ClRtlCheckForLogCorruption( szOutBuffer ) ) { Bytes = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, "LOGERROR: non-ASCII characters detected after formatting of message ID #%1!u!\n", 0, 0, szOutBuffer, sizeof(szOutBuffer) / sizeof(szOutBuffer[0]), (va_list *) &MessageId ); } ClRtlpOutputString(szOutBuffer); } } // ClRtlMsgPrint
VOID ClRtlpDbgPrint( DWORD LogLevel, PCHAR FormatString, va_list ArgList ) /*++
Routine Description:
Prints a message to the debugger or console, as appropriate.
Arguments:
LogLevel - Supplies the logging level, one of LOG_CRITICAL 1 LOG_UNUSUAL 2 LOG_NOISE 3
String - The initial message string to print.
Any FormatMessage-compatible arguments to be inserted in the ErrorMessage before it is logged.
Return Value: None.
--*/ { UNICODE_STRING UnicodeString; ANSI_STRING AnsiString; WCHAR wszOutBuffer[LOGENTRY_BUFFER_SIZE]; WCHAR wszInBuffer[LOGENTRY_BUFFER_SIZE]; CHAR szOutBuffer[LOGENTRY_BUFFER_SIZE]; NTSTATUS Status; DWORD Bytes;
//
// don't go any further if nothing to do
//
if ( !ClRtlpIsOutputDeviceAvailable()) { return; }
//
// next check that this message isn't filtered out by the current logging
// level
//
if ( ClRtlDbgLogLevel != NULL ) { if ( LogLevel > *ClRtlDbgLogLevel ) { return; } }
RtlInitAnsiString( &AnsiString, FormatString ); UnicodeString.MaximumLength = LOGENTRY_BUFFER_SIZE; UnicodeString.Buffer = wszInBuffer; Status = RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, FALSE ); if ( !NT_SUCCESS( Status ) ) { return; }
try { Bytes = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, UnicodeString.Buffer, 0, 0, wszOutBuffer, sizeof(wszOutBuffer) / sizeof(wszOutBuffer[0]), &ArgList); } except ( EXCEPTION_EXECUTE_HANDLER ) { Bytes = FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, L"LOGERROR(exception): Could not print message: %1!hs!.", 0, 0, wszOutBuffer, sizeof(wszOutBuffer) / sizeof(wszOutBuffer[0]), (va_list *) &FormatString ); }
if (Bytes != 0) { UnicodeString.Length = (USHORT) Bytes * sizeof(WCHAR); UnicodeString.Buffer = wszOutBuffer; AnsiString.MaximumLength = LOGENTRY_BUFFER_SIZE; AnsiString.Buffer = szOutBuffer; Status = RtlUnicodeStringToAnsiString( &AnsiString, &UnicodeString, FALSE ); if ( NT_SUCCESS( Status ) ) { if ( ClRtlCheckForLogCorruption( AnsiString.Buffer ) ) { ClRtlpOutputString(szOutBuffer); } else { Bytes = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, "LOGERROR: non-ASCII characters in formatted message: %1!hs!", 0, 0, szOutBuffer, sizeof(szOutBuffer) / sizeof(szOutBuffer[0]), (va_list *) &FormatString );
if ( Bytes > 0 ) { ClRtlpOutputString(szOutBuffer); if ( szOutBuffer[ Bytes - 1 ] != '\n' ) { ClRtlpOutputString( "\n" ); } } } } }
} // ClRtlpDbgPrint
VOID ClRtlDbgPrint( DWORD LogLevel, PCHAR FormatString, ... ) /*++
Routine Description:
Prints a message to the debugger or console, as appropriate.
Arguments:
LogLevel - Supplies the logging level, one of LOG_CRITICAL 1 LOG_UNUSUAL 2 LOG_NOISE 3
String - The initial message string to print.
Any FormatMessage-compatible arguments to be inserted in the ErrorMessage before it is logged.
Return Value: None.
--*/ { va_list ArgList;
va_start(ArgList, FormatString); ClRtlpDbgPrint( LogLevel, FormatString, ArgList ); va_end(ArgList);
} // ClRtlDbgPrint
VOID ClRtlPrintf( PCHAR FormatString, ... ) /*++
Routine Description:
Prints a message to the debugger or console, as appropriate.
Arguments:
Just like printf
Return Value: None.
--*/ { char buf[128]; va_list ArgList;
va_start(ArgList, FormatString); _vsnprintf(buf, sizeof(buf), FormatString, ArgList); buf[127] = 0; ClRtlLogPrint( 1, "%1!hs!", buf); va_end(ArgList);
} // ClRtlDbgPrint
DWORD ClRtlpTruncateFile( IN HANDLE FileHandle, IN DWORD FileSize, IN LPDWORD LastPosition )
/*++
Routine Description:
Truncate a file from the front.
Arguments:
FileHandle - File handle.
FileSize - Current End of File.
LastPosition - Move from this last position to end-of-file to beginning.
Return Value:
New end of file.
--*/
{ //
// The following buffer size should never be more than 1/4 the size of the
// file.
//
#define BUFFER_SIZE ( 64 * 1024 )
DWORD bytesLeft; DWORD endPosition = 0; DWORD bufferSize; DWORD bytesRead; DWORD bytesWritten; DWORD fileSizeHigh = 0; DWORD readPosition; DWORD writePosition; PVOID dataBuffer;
if ( *LastPosition >= FileSize ) { goto error_exit; }
bytesLeft = FileSize - *LastPosition; dataBuffer = LocalAlloc( LMEM_FIXED, BUFFER_SIZE ); if ( !dataBuffer ) { goto error_exit; } endPosition = bytesLeft;
//
// Point back to last position for reading.
//
readPosition = *LastPosition; writePosition = 0;
while ( bytesLeft ) { if ( bytesLeft >= BUFFER_SIZE ) { bufferSize = BUFFER_SIZE; } else { bufferSize = bytesLeft; } bytesLeft -= bufferSize; SetFilePointer( FileHandle, readPosition, &fileSizeHigh, FILE_BEGIN ); if ( ReadFile( FileHandle, dataBuffer, bufferSize, &bytesRead, NULL ) ) {
SetFilePointer( FileHandle, writePosition, &fileSizeHigh, FILE_BEGIN ); WriteFile( FileHandle, dataBuffer, bytesRead, &bytesWritten, NULL ); } else { endPosition = 0; break; } readPosition += bytesRead; writePosition += bytesWritten; }
LocalFree( dataBuffer );
error_exit:
//
// Force end of file to get set.
//
SetFilePointer( FileHandle, endPosition, &fileSizeHigh, FILE_BEGIN );
SetEndOfFile( FileHandle );
*LastPosition = endPosition;
return(endPosition);
} // ClRtlpTruncateFile
VOID ClRtlLogPrint( ULONG LogLevel, PCHAR FormatString, ... ) /*++
Routine Description:
Prints a message to a log file.
Arguments:
LogLevel - Supplies the logging level, one of LOG_CRITICAL 1 LOG_UNUSUAL 2 LOG_NOISE 3
String - The initial message string to print.
Any FormatMessage-compatible arguments to be inserted in the ErrorMessage before it is logged.
Return Value:
None.
--*/ { UNICODE_STRING UnicodeString; ANSI_STRING AnsiString; WCHAR wszInBuffer[LOGENTRY_BUFFER_SIZE]; WCHAR wszOutBuffer[LOGENTRY_BUFFER_SIZE]; CHAR szOutBuffer[LOGENTRY_BUFFER_SIZE]; DWORD Bytes; DWORD PrefixBytes; DWORD BytesWritten; DWORD FileSize; DWORD FileSizeHigh; NTSTATUS Status; SYSTEMTIME Time; ULONG_PTR ArgArray[9]; va_list ArgList; PWCHAR logLabel;
//
// init the variable arg list
//
va_start(ArgList, FormatString);
ClRtlpDbgPrint( LogLevel, FormatString, ArgList );
if ( !ClRtlPrintToFile ) { va_end(ArgList); return; }
// begin_wpp config
// CUSTOM_TYPE(level, ItemListByte(UNK0, ERR_, WARN, INFO) );
// end_wpp
//
// convert nuemric LogLevel to something readable
//
switch ( LogLevel ) { case LOG_NOISE: logLabel = L"INFO "; break;
case LOG_UNUSUAL: logLabel = L"WARN "; break;
case LOG_CRITICAL: logLabel = L"ERR "; break;
default: ASSERT( 0 ); logLabel = L"UNKN "; break; }
GetSystemTime(&Time);
ArgArray[0] = ClRtlProcessId; ArgArray[1] = GetCurrentThreadId(); ArgArray[2] = Time.wYear; ArgArray[3] = Time.wMonth; ArgArray[4] = Time.wDay; ArgArray[5] = Time.wHour; ArgArray[6] = Time.wMinute; ArgArray[7] = Time.wSecond; ArgArray[8] = Time.wMilliseconds;
PrefixBytes = FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, L"%1!08lx!.%2!08lx!::%3!02d!/%4!02d!/%5!02d!-%6!02d!:%7!02d!:%8!02d!.%9!03d! ", 0, 0, wszOutBuffer, sizeof(wszOutBuffer)/sizeof(wszOutBuffer[0]), (va_list*)&ArgArray);
if ( PrefixBytes == 0 ) { va_end(ArgList); WmiTrace("Prefix format failed, %d: %!ARSTR!", GetLastError(), FormatString); return; }
//
// add on the log label at the end and adjust PrefixBytes
//
wcscat( wszOutBuffer, logLabel ); PrefixBytes = wcslen( wszOutBuffer );
// convert in the message into unicode
RtlInitAnsiString( &AnsiString, FormatString ); UnicodeString.MaximumLength = LOGENTRY_BUFFER_SIZE; UnicodeString.Buffer = wszInBuffer; Status = RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, FALSE ); if ( !NT_SUCCESS( Status ) ) { va_end(ArgList); WmiTrace("AnsiToUni failed, %x: %!ARSTR!", Status, FormatString); return; }
try { Bytes = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, UnicodeString.Buffer, 0, 0, &wszOutBuffer[PrefixBytes], (sizeof(wszOutBuffer) / sizeof(wszOutBuffer[0])) - PrefixBytes, &ArgList); } except ( EXCEPTION_EXECUTE_HANDLER ) { Bytes = FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, L"LOGERROR(exception): Could not print message: %1!hs!", 0, 0, &wszOutBuffer[PrefixBytes], (sizeof(wszOutBuffer) / sizeof(wszOutBuffer[0])) - PrefixBytes, (va_list *) &FormatString ); } va_end(ArgList);
if (Bytes != 0) {
// convert the out to Ansi
UnicodeString.Buffer = wszOutBuffer; UnicodeString.Length = ((USHORT) Bytes + (USHORT) PrefixBytes) * sizeof(WCHAR); AnsiString.Buffer = szOutBuffer; AnsiString.MaximumLength = LOGENTRY_BUFFER_SIZE; Status = RtlUnicodeStringToAnsiString( &AnsiString, &UnicodeString, FALSE ); if ( !NT_SUCCESS( Status ) ) { WmiTrace("UniToAnsi failed, %x: %!ARWSTR!", Status, wszOutBuffer + PrefixBytes); return; }
ClRtlAcquirePrintLock();
FileSize = GetFileSize( ClRtlPrintFile, &FileSizeHigh ); ASSERT( FileSizeHigh == 0 ); // We're only using DWORDs!
if ( FileSize > ClRtlPrintFileLimit ) { FileSize = ClRtlpTruncateFile( ClRtlPrintFile, FileSize, &ClRtlPrintFileLoWater ); }
SetFilePointer( ClRtlPrintFile, FileSize, &FileSizeHigh, FILE_BEGIN ); if ( ClRtlCheckForLogCorruption( AnsiString.Buffer ) ) { #if defined(ENCRYPT_TEXT_LOG)
int i; for (i = 0; i < AnsiString.Length; ++i) { AnsiString.Buffer[i] ^= 'a'; } #endif
WriteFile(ClRtlPrintFile, AnsiString.Buffer, AnsiString.Length, &BytesWritten, NULL); #if defined(ENCRYPT_TEXT_LOG)
for (i = 0; i < AnsiString.Length; ++i) { AnsiString.Buffer[i] ^= 'a'; } #endif
} else { Bytes = FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, "LOGERROR: non-ASCII characters in formatted message: %1!hs!", 0, 0, &szOutBuffer[PrefixBytes], (sizeof(szOutBuffer) / sizeof(szOutBuffer[0])) - PrefixBytes, (va_list *) &FormatString );
if ( Bytes > 0 ) { WriteFile(ClRtlPrintFile, szOutBuffer, PrefixBytes + Bytes, &BytesWritten, NULL);
if ( szOutBuffer[ PrefixBytes + Bytes - 1 ] != '\n' ) { WriteFile(ClRtlPrintFile, "\n", 1, &BytesWritten, NULL); }
RtlInitAnsiString( &AnsiString, szOutBuffer ); } }
if ( (ClRtlPrintFileLoWater == 0) && (FileSize > (ClRtlPrintFileLimit / 2)) ) { ClRtlPrintFileLoWater = FileSize + BytesWritten; }
ClRtlReleasePrintLock();
WmiTrace("%!level! %!ARSTR!", *(UCHAR*)&LogLevel, AnsiString.Buffer + PrefixBytes); /*
#if defined(WMI_TRACING)
if (ClRtlWml.Trace && ClRtlWmiReg.EnableFlags) { ClRtlWml.Trace(10, &ClRtlTraceGuid, ClRtlWmiReg.LoggerHandle, LOG(UINT, ClRtlProcessId) LOGASTR(AnsiString.Buffer + PrefixBytes) 0); } #endif // defined(WMI_TRACING)
*/ } else { WmiTrace("Format returned 0 bytes: %!ARSTR!", FormatString); } return; } // ClRtlLogPrint
VOID ClRtlpFlushLogBuffers( VOID )
/*++
Routine Description:
Flush the cluster log file
Arguments:
none
Return Value:
none
--*/
{ FlushFileBuffers( ClRtlPrintFile ); }
DWORD ClRtlCreateDirectory( IN LPCWSTR lpszPath ) /*++
Routine Description:
Creates a directory creating any subdirectories as required.
Arguments:
lpszMultiSz - Supplies the path to the directory. It may or may not be terminated by a back slash.
Return Value:
ERROR_SUCCESS if successful, else the error code.
--*/ { WCHAR cSlash = L'\\'; DWORD dwLen; LPCWSTR pszNext; WCHAR lpszDir[MAX_PATH]; LPWSTR pszDirPath=NULL; DWORD dwError = ERROR_SUCCESS;
if (!lpszPath || ((dwLen=lstrlenW(lpszPath)) < 1)) { dwError = ERROR_INVALID_PARAMETER; goto FnExit; }
pszDirPath = (LPWSTR)LocalAlloc(LMEM_FIXED, ((dwLen + 2) * sizeof(WCHAR))); if (pszDirPath == NULL) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto FnExit; } lstrcpyW(pszDirPath, lpszPath);
//if it doesnt terminate with \, terminate it
if (pszDirPath[dwLen-1] != cSlash) { pszDirPath[dwLen] = cSlash; pszDirPath[dwLen+1] = L'\0'; }
dwLen = lstrlenW(pszDirPath); //handle SMB Path names e.g \\xyz\abc\lmn
if ((dwLen > 2) && (pszDirPath[0]== L'\\') && (pszDirPath[1] == L'\\')) { //check if the name if of format \\?\UNC\XYZ\ABC\LMN
// or if the format \\?\C:\xyz\abz
if ((dwLen >3) && (pszDirPath[2] == L'?')) { //search for the \ after ?
pszNext = wcschr(pszDirPath + 2, cSlash); //check if it is followed by UNC
if (pszNext) { if (!wcsncmp(pszNext+1, L"UNC", lstrlenW(L"UNC"))) { //it is a UNC Path name
//move past the third slash from here
pszNext = wcschr(pszNext+1, cSlash); if (pszNext) pszNext = wcschr(pszNext+1, cSlash); if (pszNext) pszNext = wcschr(pszNext+1, cSlash); } else { //it is a volume name, move to the next slash
pszNext = wcschr(pszNext+1, cSlash); } } } else { //it is of type \\xyz\abc\lmn
pszNext = wcschr(pszDirPath + 2, cSlash); if (pszNext) pszNext = wcschr(pszNext+1, cSlash); } } else { pszNext = pszDirPath; pszNext = wcschr(pszNext, cSlash); // if the character before the first \ is :, skip the creation
// of the c:\ level directory
if (pszNext && pszNext > pszDirPath) { pszNext--; if (*pszNext == L':') { pszNext++; pszNext = wcschr(pszNext+1, cSlash); } else pszNext++; } } while ( pszNext) { DWORD_PTR dwptrLen;
dwptrLen = pszNext - pszDirPath + 1;
dwLen=(DWORD)dwptrLen; lstrcpynW(lpszDir, pszDirPath, dwLen+1);
if (!CreateDirectory(lpszDir, NULL)) { dwError = GetLastError(); if (dwError == ERROR_ALREADY_EXISTS) { //this is not a problem,continue
dwError = ERROR_SUCCESS; } else { ClRtlDbgPrint(LOG_CRITICAL, "[ClRtl] CreateDirectory Failed on %1!ws!. Error %2!u!\n", lpszDir, dwError); goto FnExit; } }
pszNext = wcschr(pszNext+1, cSlash); }
FnExit: if (pszDirPath) LocalFree(pszDirPath); return(dwError); }
BOOL WINAPI ClRtlIsPathValid( LPCWSTR Path )
/*++
Routine Description:
Returns true if the given path looks syntactically valid.
This call is NOT network-aware.
Arguments:
Path - String containing a path.
Return Value:
TRUE if the path looks valid, otherwise FALSE.
--*/
{ WCHAR chPrev; WCHAR chCur; DWORD charCount = 0; #ifdef DBCS
BOOL fPrevLead = FALSE; #endif
CL_ASSERT(Path); CL_ASSERT(!*Path || !iswspace(*Path)); // no leading whitespace
if ( iswalpha(*Path) && *(Path+1) == L':' ) { Path += 2; }
chCur = *Path; chPrev = 0;
while (chCur) { charCount++; if ( charCount > MAX_PATH ) { return(FALSE); } #ifdef DBCS
if (fPrevLead) { fPrevLead = FALSE; chPrev = 0; } else { fPrevLead = IsDBCSLeadByte(chCur); #endif // DBCS
switch ( chCur ) {
// Explicit invalid characters
case L'*' : case L';' : case L',' : case L'=' : case L'?' : case L'<' : case L'>' : case L'|' : case L':' : // no ":" except as second char
return(FALSE); // no ":" allowed other than second char */
#if 0 // The following should be okay
case L'\\' : if ( chPrev == chDirSep ) { return(FALSE); // no double "\\" in middle - but legal
} break; #endif
default: #if 0 // accept anything else for now
if ( !iswalnum( chCur ) ) { return(FALSE); } #endif
break; }
chPrev = chCur;
#ifdef DBCS
} #endif
chCur = *(++Path); }
#ifdef DBCS
if (fPrevLead) return(FALSE); // ends w/ lead byte
#endif
return(TRUE);
} // ClRtlIsPathValid
/****
@func DWORD | ClRtlGetClusterDirectory | Get the directory in which the cluster service is installed
@parm IN LPWSTR | lpBuffer | Supplies the buffer in which the directory path is to be copied.
@parm IN DWORD | dwBufSize | Supplies the size of the buffer.
@rdesc Returns a Win32 error code if the operation is unsuccessful. ERROR_SUCCESS on success. ****/ DWORD ClRtlGetClusterDirectory( IN LPWSTR lpBuffer, IN DWORD dwBufSize ) { DWORD dwLen; DWORD dwStatus; LPWSTR szRegKeyName = NULL; HKEY hClusSvcKey = NULL; LPWSTR lpImagePath = NULL; WCHAR *pTemp = NULL;
//
// Chittur Subbaraman (chitturs) - 10/29/98
//
if ( lpBuffer == NULL ) { dwStatus = ERROR_INVALID_PARAMETER; goto FnExit; } //
// Open key to SYSTEM\CurrentControlSet\Services\ClusSvc
//
dwLen = lstrlenW( CLUSREG_KEYNAME_CLUSSVC_PARAMETERS ); szRegKeyName = (LPWSTR) LocalAlloc ( LMEM_FIXED, ( dwLen + 1 ) * sizeof ( WCHAR ) ); if ( szRegKeyName == NULL ) { dwStatus = GetLastError(); goto FnExit; }
dwLen -= lstrlenW( CLUSREG_KEYNAME_PARAMETERS );
lstrcpyW( szRegKeyName, CLUSREG_KEYNAME_CLUSSVC_PARAMETERS ); szRegKeyName [dwLen-1] = L'\0';
if ( ( dwStatus = RegOpenKeyW( HKEY_LOCAL_MACHINE, szRegKeyName, &hClusSvcKey ) ) != ERROR_SUCCESS ) { goto FnExit; }
lstrcpyW ( szRegKeyName, L"ImagePath" ); //
// Try to query the clussvc key. If the ImagePath
// value is present, then get the length of the image
// path
//
dwLen = 0; if ( ( dwStatus = ClRtlRegQueryString( hClusSvcKey, szRegKeyName, REG_EXPAND_SZ, &lpImagePath, &dwLen, &dwLen ) ) != ERROR_SUCCESS ) { goto FnExit; }
//
// Now expand any environment strings present in the
// ImagePath
//
if ( ( dwLen = ExpandEnvironmentStringsW( lpImagePath, lpBuffer, dwBufSize ) ) == 0 ) { dwStatus = GetLastError(); goto FnExit; }
//
// If the caller-supplied buffer is not big enough to hold the
// path value, then return an error
//
if ( dwLen > dwBufSize ) { dwStatus = ERROR_INVALID_PARAMETER; goto FnExit; }
//
// Replace the last '\\' character in the image path with
// a NULL character
//
pTemp = wcsrchr( lpBuffer, L'\\' ); if ( pTemp != NULL ) { *pTemp = L'\0'; } else { dwStatus = ERROR_INVALID_PARAMETER; goto FnExit; }
FnExit: LocalFree( szRegKeyName ); if( hClusSvcKey != NULL ) { RegCloseKey( hClusSvcKey ); } LocalFree( lpImagePath );
return( dwStatus ); } // ClRtlGetClusterDirectory
BOOL ClRtlGetDriveLayoutTable( IN HANDLE hDisk, OUT PDRIVE_LAYOUT_INFORMATION * DriveLayout, OUT PDWORD InfoSize OPTIONAL )
/*++
Routine Description:
Get the partition table for a drive. If the buffer is not large enough, then realloc until we get the right sized buffer. This routine is not in disk.cpp since that causes additional symbols to be defined.
Arguments:
hDisk - handle to a file on the partition
DriveLayout - address of pointer that points to
InfoSize - address of dword that receives size of partition table
Return Value:
TRUE if everything went ok
--*/
{ DWORD dwSize = 0; PDRIVE_LAYOUT_INFORMATION driveLayout; DWORD status = ERROR_INSUFFICIENT_BUFFER; DWORD partitionCount = 4; DWORD layoutSize;
while ( status == ERROR_INSUFFICIENT_BUFFER || status == ERROR_BAD_LENGTH ) {
layoutSize = sizeof(DRIVE_LAYOUT_INFORMATION) + (sizeof(PARTITION_INFORMATION) * partitionCount); if ( layoutSize > 2<<16 ) { break; }
driveLayout = (PDRIVE_LAYOUT_INFORMATION)LocalAlloc( LMEM_FIXED, layoutSize ); if ( driveLayout == NULL ) { break; }
if (DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0, driveLayout, layoutSize, &dwSize, NULL)) { status = ERROR_SUCCESS; break; } else { status = GetLastError(); LocalFree( driveLayout ); driveLayout = NULL; partitionCount *= 2; } }
*DriveLayout = driveLayout; if ( ARGUMENT_PRESENT( InfoSize )) { *InfoSize = dwSize; }
return status == ERROR_SUCCESS ? TRUE : FALSE;
} // ClRtlGetDriveLayoutTable
BOOL ClRtlPathFileExists( LPWSTR pwszPath ) /*++
Routine Description:
Determines if a file/directory exists. This is fast.
Arguments:
pwszPath - Path to validate.
Return Value:
TRUE if it exists, otherwise FALSE.
NOTE: This was borrowed from SHLWAPI.
--*/ { DWORD dwErrMode; BOOL fResult;
dwErrMode = SetErrorMode( SEM_FAILCRITICALERRORS );
fResult = ( (UINT) GetFileAttributes( pwszPath ) != (UINT) -1 );
SetErrorMode( dwErrMode );
return fResult;
}
DWORD SetClusterFailureInformation( LPWSTR NodeName OPTIONAL, DWORD ResetPeriod, LONG RetryCount, DWORD RetryInterval )
/*++
Routine Description:
Set the SC failure parameters for the cluster service.
Arguments:
The args are loosely similar to the members of the SERVICE_FAILURE_ACTIONS structure. If RetryCount equals -1, then we set up a series of actions where the SC will exponentially back off restarting the service until it reaches 5 minutes, where it will continue to retry forever (well, until something good or bad happens). Otherwise, if RetryCount is positive, then we'll retry that many times (and zero is a valid number of retries) still using the same back off technique.
Return Value:
ERROR_SUCCESS if everything worked ok
--*/
{ DWORD status; BOOL success; HANDLE schSCManager; HANDLE serviceHandle; SERVICE_FAILURE_ACTIONS failureData; LPSC_ACTION failureActions; LONG i; BOOL tryForever = FALSE;
CL_ASSERT( RetryCount >= -1 && RetryCount <= CLUSTER_FAILURE_MAX_STARTUP_RETRIES );
++RetryCount; // add one more for the final action
if ( RetryCount == 0 ) { DWORD tempInterval = RetryInterval;
//
// count the entries we need to go from our initial retry interval to
// the final (longest) retry interval.
//
while ( tempInterval < CLUSTER_FAILURE_FINAL_RETRY_INTERVAL ) { tempInterval *= 2; ++RetryCount; }
++RetryCount; tryForever = TRUE; } CL_ASSERT( RetryCount > 0 );
//
// open the SC mgr and the service
//
schSCManager = OpenSCManager(NodeName, NULL, // database (NULL == default)
SC_MANAGER_ALL_ACCESS); // access required
if ( schSCManager ) { serviceHandle = OpenService(schSCManager, CLUSTER_SERVICE_NAME, SERVICE_ALL_ACCESS);
if ( serviceHandle ) {
failureActions = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, RetryCount * sizeof( SC_ACTION )); if ( failureActions != NULL ) {
//
// build a list that exponentially backs off but does
// exactly the number of retries that were specified.
//
for ( i = 0; i < RetryCount-1; ++i ) { failureActions[i].Type = SC_ACTION_RESTART; failureActions[i].Delay = RetryInterval;
RetryInterval = RetryInterval * 2; if ( RetryInterval > CLUSTER_FAILURE_FINAL_RETRY_INTERVAL ) { RetryInterval = CLUSTER_FAILURE_FINAL_RETRY_INTERVAL; } }
if ( tryForever ) { failureActions[i].Type = SC_ACTION_RESTART; failureActions[i].Delay = RetryInterval; } else { failureActions[i].Type = SC_ACTION_NONE; failureActions[i].Delay = 0; }
failureData.dwResetPeriod = ResetPeriod; failureData.lpRebootMsg = NULL; failureData.lpCommand = NULL; failureData.cActions = RetryCount; failureData.lpsaActions = failureActions;
success = ChangeServiceConfig2(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, &failureData); LocalFree( failureActions );
if ( success ) { status = ERROR_SUCCESS; } else { status = GetLastError(); ClRtlDbgPrint(LOG_CRITICAL,"[ClRtl] Couldn't set SC failure info %1!u!\n", status); } } else { status = ERROR_OUTOFMEMORY; ClRtlDbgPrint(LOG_CRITICAL,"[ClRtl] Couldn't allocate memory to set SM Failure actions\n"); }
CloseServiceHandle( serviceHandle ); } else { status = GetLastError(); ClRtlDbgPrint(LOG_CRITICAL,"[ClRtl] Couldn't get SC handle to Cluster Service %1!u!\n", status); }
CloseServiceHandle( schSCManager ); } else { status = GetLastError(); ClRtlDbgPrint(LOG_CRITICAL,"[ClRtl] Couldn't get a handle to the SC Manager %1!u!\n", status); }
return status; } // SetClusterFailureInformation
DWORD ClRtlSetSCMFailureActions( LPWSTR NodeName OPTIONAL )
/*++
Routine Description:
Set the service controller failure parameters for the cluster service.
Arguments:
NodeName - pointer to string that identifies on which node to modify the settings. NULL indicates the local node.
Return Value:
ERROR_SUCCESS if everything worked ok
--*/
{ DWORD Status;
//
// during startup, we start with a short retry period and then
// exponentially back off. Set the reset period to 30 minutes.
//
Status = SetClusterFailureInformation(NodeName, 30 * 60, CLUSTER_FAILURE_RETRY_COUNT, CLUSTER_FAILURE_INITIAL_RETRY_INTERVAL);
if ( Status != ERROR_SUCCESS ) { ClRtlDbgPrint(LOG_CRITICAL, "[ClRtl] Couldn't set SC startup failure info %1!u!\n", Status); }
return Status; } // ClRtlSetSCMFailureActions
DWORD ClRtlGetRunningAccountInfo( LPWSTR * AccountBuffer )
/*++
Routine Description:
Get the calling thread's token to obtain account info. It is returned in an allocated buffer in the form of "domain\user". Caller is responsible for freeing the buffer.
Arguments:
AccountBuffer - address of pointer to receive allocated buffer
Return Value:
ERROR_SUCCESS if everything worked ok
--*/
{ HANDLE currentToken; PTOKEN_USER tokenUserData; DWORD sizeRequired; BOOL success; DWORD status = ERROR_SUCCESS; DWORD accountNameSize = 128; LPWSTR accountName; DWORD domainNameSize = DNS_MAX_NAME_BUFFER_LENGTH; LPWSTR domainName; SID_NAME_USE sidType; DWORD nameSize = 0; HMODULE secur32Handle; FARPROC getUserNameEx; INT_PTR returnValue;
//
// initialize in case the caller doesn't check the return status (tsk, tsk!)
//
*AccountBuffer = NULL;
//
// rather than link in yet another DLL, we'll load secur32 dynamically and
// get a pointer to GetUserNameEx.
//
secur32Handle = LoadLibraryW( L"secur32.dll" ); if ( secur32Handle ) { getUserNameEx = GetProcAddress( secur32Handle, "GetUserNameExW" ); if ( getUserNameEx ) { //
// get the length the first time, allocate a buffer and then get the data
//
returnValue = (*getUserNameEx)( NameSamCompatible, NULL, &nameSize ); success = (BOOL)returnValue;
*AccountBuffer = LocalAlloc( LMEM_FIXED, nameSize * sizeof( WCHAR )); if ( *AccountBuffer != NULL ) {
returnValue = (*getUserNameEx)( NameSamCompatible, *AccountBuffer, &nameSize ); success = (BOOL)returnValue; if ( !success ) { status = GetLastError(); } } else { status = GetLastError(); } } else { status = GetLastError(); }
FreeLibrary( secur32Handle ); } else { status = GetLastError(); }
return status;
#if 0
//
// check if there is a thread token
//
if (!OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, ¤tToken)) { // get the process token
if (!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, ¤tToken )) { return GetLastError(); } }
//
// get the size needed
//
success = GetTokenInformation(currentToken, TokenUser, NULL, 0, &sizeRequired);
tokenUserData = LocalAlloc( LMEM_FIXED, sizeRequired ); if ( tokenUserData == NULL ) { CloseHandle( currentToken ); return GetLastError(); }
success = GetTokenInformation(currentToken, TokenUser, tokenUserData, sizeRequired, &sizeRequired);
if ( !success ) { CloseHandle( currentToken ); return GetLastError(); }
do { //
// make initial allocs for account and domain name; 1 more byte to
// hold slash separator. domain buffer holds the complete
// 'domain\user' entry so it gets more space
//
domainName = LocalAlloc( LMEM_FIXED, (accountNameSize + domainNameSize + 1) * sizeof(WCHAR) ); accountName = (LPWSTR) LocalAlloc( LMEM_FIXED, accountNameSize * sizeof(WCHAR) );
if ( accountName == NULL || domainName == NULL ) { if ( accountName != NULL ) { LocalFree( accountName ); }
if ( domainName != NULL ) { LocalFree( domainName ); }
return ERROR_NOT_ENOUGH_MEMORY; }
//
// Attempt to Retrieve the account and domain name. If
// LookupAccountName fails because of insufficient buffer size(s)
// accountNameSize and domainNameSize will be correctly set for the
// next attempt.
//
if ( LookupAccountSid(NULL, tokenUserData->User.Sid, accountName, &accountNameSize, domainName, &domainNameSize, &sidType )) { wcscat( domainName, L"\\" ); wcscat( domainName, accountName ); *AccountBuffer = domainName; } else { // free the account name buffer and find out why we failed
LocalFree( domainName );
status = GetLastError(); }
//
// domain buffer holds complete string so we can lose the account name
// at this point
//
LocalFree( accountName ); accountName = NULL;
} while ( status == ERROR_INSUFFICIENT_BUFFER );
return status; #endif
} // ClRtlGetRunningAccountInfo
#if 0
//
// no longer needed. Keep it around just in case
//
DWORD ClRtlGetServiceAccountInfo( LPWSTR * AccountBuffer )
/*++
Routine Description:
Query SCM for the cluster service account info. It is returned in an allocated buffer in the form of "domain\user". Caller is responsible for freeing the buffer.
Arguments:
AccountBuffer - address of pointer to receive allocated buffer
Return Value:
ERROR_SUCCESS if everything worked ok
--*/
{ DWORD status = ERROR_SUCCESS; HANDLE schSCManager; HANDLE serviceHandle = NULL; LPQUERY_SERVICE_CONFIG scConfigData = NULL; ULONG bytesNeeded; BOOL success;
//
// open a handle to the service controller manager to query the account
// under which the cluster service was started
//
schSCManager = OpenSCManager(NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_ALL_ACCESS); // access required
if ( schSCManager == NULL ) { status = GetLastError(); goto error_exit; }
serviceHandle = OpenService(schSCManager, CLUSTER_SERVICE_NAME, SERVICE_ALL_ACCESS);
if ( serviceHandle == NULL ) { status = GetLastError(); goto error_exit; }
success = QueryServiceConfig(serviceHandle, NULL, 0, &bytesNeeded); if ( !success ) { status = GetLastError(); if ( status != ERROR_INSUFFICIENT_BUFFER ) { goto error_exit; } else { status = ERROR_SUCCESS; } }
scConfigData = LocalAlloc( LMEM_FIXED, bytesNeeded ); if ( scConfigData == NULL ) { status = GetLastError(); goto error_exit; }
success = QueryServiceConfig(serviceHandle, scConfigData, bytesNeeded, &bytesNeeded); if ( !success ) { status = GetLastError(); goto error_exit; }
*AccountBuffer = LocalAlloc(LMEM_FIXED, (wcslen( scConfigData->lpServiceStartName ) + 1 ) * sizeof(WCHAR));
if ( *AccountBuffer == NULL ) { status = GetLastError(); goto error_exit; }
wcscpy( *AccountBuffer, scConfigData->lpServiceStartName );
error_exit: if ( serviceHandle != NULL ) { CloseServiceHandle( serviceHandle ); }
if ( schSCManager != NULL ) { CloseServiceHandle( schSCManager ); }
if ( scConfigData != NULL ) { LocalFree( scConfigData ); }
return status; } // ClRtlGetServiceAccountInfo
#endif
|