/*++ 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