mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2460 lines
66 KiB
2460 lines
66 KiB
/*++
|
|
|
|
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
|