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.
981 lines
29 KiB
981 lines
29 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
omlog.c
|
|
|
|
Abstract:
|
|
|
|
logging routines for the object log stream
|
|
|
|
this code was jumpstarted from the clusrtl logging code. Most notably, it
|
|
is UTF-8 encoded which allows full Unicode without the cost of writing
|
|
16b. for each character.
|
|
|
|
remaining issues to solve: proper truncation of log file based on current
|
|
starting session. We'd like to remember as many sessions as possible but
|
|
since I've chosen UTF-8, I can't jump to the middle of the file and start
|
|
looking around. One possibility strategy is using an alternate NTFS stream
|
|
to record the starting offsets of sessions. If that stream didn't exist or
|
|
we're on a FAT FS, then we'll continue with the current strategy.
|
|
|
|
Author:
|
|
|
|
Charlie Wickham (charlwi) 07-May-2001
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "omp.h"
|
|
|
|
//
|
|
// when this sequence is at the beginning of the file, it indicates that the
|
|
// file is UTF8 encoded
|
|
//
|
|
#define UTF8_BOM "\x0EF\x0BB\x0BF"
|
|
|
|
//
|
|
// Private Data
|
|
//
|
|
|
|
DWORD OmpLogFileLimit;
|
|
DWORD OmpCurrentSessionStart;
|
|
BOOL OmpLogToFile = FALSE;
|
|
HANDLE OmpLogFileHandle = NULL;
|
|
DWORD OmpProcessId;
|
|
DWORD OmpCurrentSessionOffset;
|
|
|
|
PCLRTL_WORK_QUEUE OmpLoggerWorkQueue;
|
|
|
|
//
|
|
// structure used to pass formatted buffers to work queue routine
|
|
//
|
|
typedef struct _OM_LOG_BUFFER_DESC {
|
|
DWORD TimeBytes;
|
|
DWORD MsgBytes;
|
|
PCHAR TimeBuffer;
|
|
PCHAR MsgBuffer;
|
|
} OM_LOG_BUFFER_DESC, *POM_LOG_BUFFER_DESC;
|
|
|
|
#define MAX_NUMBER_LENGTH 20
|
|
|
|
// Specify maximum file size ( DWORD / 1MB )
|
|
|
|
#define MAX_FILE_SIZE ( 0xFFFFF000 / ( 1024 * 1024 ) )
|
|
|
|
DWORD OmpLogFileLimit = ( 1 * 1024 * 1024 ); // 1 MB default
|
|
DWORD OmpLogFileLoWater = 0;
|
|
|
|
//
|
|
// internal functions
|
|
//
|
|
DWORD
|
|
OmpTruncateFile(
|
|
IN HANDLE FileHandle,
|
|
IN DWORD FileSize,
|
|
IN LPDWORD LastPosition
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Truncate a file by copying the portion starting at LastPosition and ending
|
|
at EOF to the front of the file and setting the file's EOF pointer to the
|
|
end of the new chunk. We always keep the current session even that means
|
|
growing larger than the file limit.
|
|
|
|
For now, we have no good way of finding all the sessions within the file,
|
|
so if the file must be truncated, we whack it back to the beginning of the
|
|
current session. If time permits, I'll add something more intelligent
|
|
later on.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - File handle.
|
|
|
|
FileSize - Current End of File.
|
|
|
|
LastPosition - On input, specifies the starting position in the file from
|
|
which the copy begins. On output, it is set to the new EOF
|
|
|
|
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 = sizeof( UTF8_BOM ) - 1;
|
|
DWORD bufferSize;
|
|
DWORD bytesRead;
|
|
DWORD bytesWritten;
|
|
DWORD fileSizeHigh = 0;
|
|
DWORD readPosition;
|
|
DWORD writePosition;
|
|
PVOID dataBuffer;
|
|
|
|
//
|
|
// current session is already at beginning of file so bale now...
|
|
//
|
|
if ( OmpCurrentSessionOffset == sizeof( UTF8_BOM ) - 1) {
|
|
return FileSize;
|
|
}
|
|
|
|
//
|
|
// don't truncate the current session, i.e., always copy from the start of
|
|
// the current session
|
|
//
|
|
if ( *LastPosition > OmpCurrentSessionOffset ) {
|
|
*LastPosition = OmpCurrentSessionOffset;
|
|
}
|
|
|
|
if ( *LastPosition > FileSize ) {
|
|
//
|
|
// something's confused; the spot we're supposed to copy from is at or
|
|
// past the current EOF. reset the entire file
|
|
//
|
|
goto error_exit;
|
|
}
|
|
|
|
dataBuffer = LocalAlloc( LMEM_FIXED, BUFFER_SIZE );
|
|
if ( !dataBuffer ) {
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// calc number of bytes to move
|
|
//
|
|
bytesLeft = FileSize - *LastPosition;
|
|
endPosition = bytesLeft + sizeof( UTF8_BOM ) - 1;
|
|
|
|
//
|
|
// Point back to last position for reading.
|
|
//
|
|
readPosition = *LastPosition;
|
|
writePosition = sizeof( UTF8_BOM ) - 1;
|
|
|
|
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 = sizeof( UTF8_BOM ) - 1;
|
|
break;
|
|
}
|
|
readPosition += bytesRead;
|
|
writePosition += bytesWritten;
|
|
}
|
|
|
|
LocalFree( dataBuffer );
|
|
|
|
//
|
|
// for now, we only only one truncate by setting the current position to
|
|
// the beginning of file.
|
|
//
|
|
OmpCurrentSessionOffset = sizeof( UTF8_BOM ) - 1;
|
|
|
|
error_exit:
|
|
|
|
//
|
|
// Force end of file to get set.
|
|
//
|
|
SetFilePointer( FileHandle,
|
|
endPosition,
|
|
&fileSizeHigh,
|
|
FILE_BEGIN );
|
|
|
|
SetEndOfFile( FileHandle );
|
|
|
|
*LastPosition = endPosition;
|
|
|
|
return(endPosition);
|
|
|
|
} // OmpTruncateFile
|
|
|
|
VOID
|
|
OmpLoggerWorkThread(
|
|
IN PCLRTL_WORK_ITEM WorkItem,
|
|
IN DWORD Status,
|
|
IN DWORD BytesTransferred,
|
|
IN ULONG_PTR IoContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
work queue worker routine. actually does the write to the file.
|
|
|
|
Arguments:
|
|
|
|
standard ClRtl thread args; we only care about WorkItem
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD fileSize;
|
|
DWORD fileSizeHigh;
|
|
DWORD tsBytesWritten;
|
|
DWORD msgBytesWritten;
|
|
|
|
POM_LOG_BUFFER_DESC bufDesc = (POM_LOG_BUFFER_DESC)(WorkItem->Context);
|
|
|
|
fileSize = GetFileSize( OmpLogFileHandle, &fileSizeHigh );
|
|
ASSERT( fileSizeHigh == 0 ); // We're only using DWORDs!
|
|
|
|
if ( fileSize > OmpLogFileLimit ) {
|
|
fileSize = OmpTruncateFile( OmpLogFileHandle, fileSize, &OmpLogFileLoWater );
|
|
}
|
|
|
|
SetFilePointer( OmpLogFileHandle,
|
|
fileSize,
|
|
&fileSizeHigh,
|
|
FILE_BEGIN );
|
|
|
|
WriteFile(OmpLogFileHandle,
|
|
bufDesc->TimeBuffer,
|
|
bufDesc->TimeBytes,
|
|
&tsBytesWritten,
|
|
NULL);
|
|
|
|
WriteFile(OmpLogFileHandle,
|
|
bufDesc->MsgBuffer,
|
|
bufDesc->MsgBytes,
|
|
&msgBytesWritten,
|
|
NULL);
|
|
|
|
//
|
|
// if we haven't set the lo water mark, wait until the file size has
|
|
// crossed the halfway mark and set it to the beginning of the line we
|
|
// just wrote.
|
|
//
|
|
if ( OmpLogFileLoWater == 0 && (fileSize > (OmpLogFileLimit / 2)) ) {
|
|
OmpLogFileLoWater = fileSize;
|
|
|
|
ASSERT( OmpLogFileLoWater >= OmpCurrentSessionOffset );
|
|
}
|
|
|
|
} // OmpLoggerWorkThread
|
|
|
|
VOID
|
|
OmpLogPrint(
|
|
LPWSTR FormatString,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prints a message to the object config log file.
|
|
|
|
Arguments:
|
|
|
|
FormatString - The initial message string to print.
|
|
|
|
Any FormatMessage-compatible arguments to be inserted in the message
|
|
before it is logged.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWCHAR unicodeOutput = NULL;
|
|
PCHAR timestampBuffer;
|
|
DWORD timestampBytes;
|
|
PCHAR utf8Buffer;
|
|
DWORD utf8Bytes;
|
|
PWCHAR unicodeBuffer;
|
|
DWORD unicodeBytes;
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
SYSTEMTIME Time;
|
|
ULONG_PTR ArgArray[9];
|
|
va_list ArgList;
|
|
|
|
//
|
|
// init the variable arg list
|
|
//
|
|
va_start(ArgList, FormatString);
|
|
|
|
if ( !OmpLogToFile ) {
|
|
va_end(ArgList);
|
|
return;
|
|
}
|
|
|
|
GetSystemTime(&Time);
|
|
|
|
ArgArray[0] = OmpProcessId;
|
|
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;
|
|
|
|
//
|
|
// we can get away with formatting it as ANSI since our data is all numbers
|
|
//
|
|
timestampBytes = FormatMessageA(FORMAT_MESSAGE_FROM_STRING |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
"%1!08lx!.%2!08lx!::%3!02d!/%4!02d!/%5!02d!-%6!02d!:%7!02d!:%8!02d!.%9!03d! ",
|
|
0,
|
|
0,
|
|
(LPSTR)×tampBuffer,
|
|
50,
|
|
(va_list*)ArgArray);
|
|
|
|
if ( timestampBytes == 0 ) {
|
|
va_end(ArgList);
|
|
// WmiTrace("Prefix format failed, %d: %!ARSTR!", GetLastError(), FormatString);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// format the message in unicode
|
|
//
|
|
try {
|
|
unicodeBytes = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
| FORMAT_MESSAGE_FROM_STRING,
|
|
FormatString,
|
|
0,
|
|
0,
|
|
(LPWSTR)&unicodeOutput,
|
|
512,
|
|
&ArgList);
|
|
}
|
|
except ( EXCEPTION_EXECUTE_HANDLER ) {
|
|
unicodeBytes = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
| FORMAT_MESSAGE_FROM_STRING
|
|
| FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
L"LOGERROR(exception): Could not print message: %1!ws!",
|
|
0,
|
|
0,
|
|
(LPWSTR)&unicodeOutput,
|
|
512,
|
|
(va_list *) &FormatString );
|
|
}
|
|
va_end(ArgList);
|
|
|
|
if (unicodeBytes != 0) {
|
|
PCLRTL_WORK_ITEM workQItem;
|
|
|
|
//
|
|
// convert the output to UTF-8; first get the size to see if it will
|
|
// fit in our stack buffer.
|
|
//
|
|
utf8Bytes = WideCharToMultiByte(CP_UTF8,
|
|
0, // dwFlags
|
|
unicodeOutput,
|
|
unicodeBytes,
|
|
NULL,
|
|
0,
|
|
NULL, // lpDefaultChar
|
|
NULL); // lpUsedDefaultChar
|
|
|
|
utf8Buffer = LocalAlloc( LMEM_FIXED, utf8Bytes );
|
|
if ( utf8Buffer == NULL ) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
|
|
utf8Bytes = WideCharToMultiByte(CP_UTF8,
|
|
0, // dwFlags
|
|
unicodeOutput,
|
|
unicodeBytes,
|
|
utf8Buffer,
|
|
utf8Bytes,
|
|
NULL, // lpDefaultChar
|
|
NULL); // lpUsedDefaultChar
|
|
|
|
workQItem = (PCLRTL_WORK_ITEM)LocalAlloc(LMEM_FIXED,
|
|
sizeof( CLRTL_WORK_ITEM ) + sizeof( OM_LOG_BUFFER_DESC ));
|
|
|
|
if ( workQItem != NULL ) {
|
|
POM_LOG_BUFFER_DESC bufDesc = (POM_LOG_BUFFER_DESC)(workQItem + 1);
|
|
|
|
bufDesc->TimeBytes = timestampBytes;
|
|
bufDesc->TimeBuffer = timestampBuffer;
|
|
bufDesc->MsgBytes = utf8Bytes;
|
|
bufDesc->MsgBuffer = utf8Buffer;
|
|
|
|
ClRtlInitializeWorkItem( workQItem, OmpLoggerWorkThread, bufDesc );
|
|
status = ClRtlPostItemWorkQueue( OmpLoggerWorkQueue, workQItem, 0, 0 );
|
|
|
|
} else {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto error_exit;
|
|
}
|
|
// WmiTrace("%!level! %!ARSTR!", *(UCHAR*)&LogLevel, AnsiString.Buffer + timestampBytes);
|
|
} else {
|
|
// WmiTrace("Format returned 0 bytes: %!ARSTR!", FormatString);
|
|
status = GetLastError();
|
|
}
|
|
|
|
error_exit:
|
|
if ( unicodeOutput != NULL ) {
|
|
LocalFree( unicodeOutput );
|
|
}
|
|
|
|
return;
|
|
|
|
} // OmpLogPrint
|
|
|
|
|
|
//
|
|
// exported (within OM) functions
|
|
//
|
|
|
|
VOID
|
|
OmpOpenObjectLog(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Use the clusterlog environment variable to open another file that contains
|
|
object mgr name to ID mapping info. If the routine fails, the failure is
|
|
logged in the cluster log but no logging will be done to the object log
|
|
file.
|
|
|
|
NOTE: access to the file is synchronized since this routine is assumed to
|
|
be called only once by OmInit. Arguments:
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR logFileBuffer[MAX_PATH];
|
|
LPWSTR logFileName = NULL;
|
|
WCHAR objectLogExtension[] = L".oml";
|
|
DWORD status = ERROR_SUCCESS;
|
|
DWORD defaultLogSize = 1; // in MB
|
|
DWORD envLength;
|
|
DWORD logFileNameChars;
|
|
WCHAR logFileSize[MAX_NUMBER_LENGTH];
|
|
DWORD logSize;
|
|
LPWSTR lpszBakFileName = NULL;
|
|
DWORD fileSizeHigh = 0;
|
|
DWORD fileSizeLow;
|
|
DWORD bytesWritten;
|
|
PWCHAR dot;
|
|
|
|
UNICODE_STRING logFileString;
|
|
|
|
SECURITY_ATTRIBUTES logFileSecurityAttr;
|
|
|
|
PSECURITY_DESCRIPTOR logFileSecurityDesc;
|
|
|
|
//
|
|
// see if logging has been specified; get a buffer big enough that will
|
|
// hold the object log name. If the supplied buffer to
|
|
// GetEnvironmentVariable is too small, it will return a value that
|
|
// includes the space for the trailing null, i.e., there is no need to add
|
|
// one.
|
|
//
|
|
logFileNameChars = GetEnvironmentVariable(L"ClusterLog",
|
|
logFileBuffer,
|
|
RTL_NUMBER_OF( logFileBuffer ));
|
|
|
|
if ( logFileNameChars > ( RTL_NUMBER_OF(logFileBuffer) + RTL_NUMBER_OF(objectLogExtension)) ) {
|
|
//
|
|
// allocate a larger buffer since our static one wasn't big enough
|
|
//
|
|
logFileName = LocalAlloc( LMEM_FIXED,
|
|
(logFileNameChars + RTL_NUMBER_OF(objectLogExtension)) * sizeof( WCHAR ) );
|
|
|
|
if ( logFileName == NULL ) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[OM] Unable to get memory for Object log filename buffer\n");
|
|
return;
|
|
}
|
|
|
|
logFileNameChars = GetEnvironmentVariable(L"ClusterLog",
|
|
logFileName,
|
|
logFileNameChars);
|
|
if ( logFileNameChars == 0 ) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[OM] Unable to read ClusterLog environment variable\n");
|
|
|
|
goto error_exit;
|
|
}
|
|
} else if ( logFileNameChars != 0 ) {
|
|
logFileName = logFileBuffer;
|
|
}
|
|
|
|
//
|
|
// remove any trailing white space. go to the end of the string and scan
|
|
// backwards; stop when we find the first non-white space char or we hit
|
|
// the beginning of the buffer.
|
|
//
|
|
if ( logFileName != NULL ) {
|
|
PWCHAR p = logFileName + logFileNameChars - 1;
|
|
|
|
while ( iswspace( *p )) {
|
|
*p = UNICODE_NULL;
|
|
|
|
if ( p == logFileName ) {
|
|
break;
|
|
}
|
|
|
|
--p;
|
|
}
|
|
|
|
//
|
|
// make sure something useful is left
|
|
//
|
|
if ( wcslen( logFileName ) == 0 ) {
|
|
if ( logFileName != logFileBuffer ) {
|
|
LocalFree( logFileName );
|
|
}
|
|
|
|
logFileName = NULL;
|
|
}
|
|
}
|
|
|
|
if ( logFileName == NULL ) {
|
|
//
|
|
// logging is turned off or we can't determine where to put the file.
|
|
//
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Try to get a limit on the log file size. This number is the number of
|
|
// MB.
|
|
//
|
|
envLength = GetEnvironmentVariable(L"ClusterLogSize",
|
|
logFileSize,
|
|
RTL_NUMBER_OF( logFileSize ));
|
|
|
|
if ( envLength != 0 && envLength < MAX_NUMBER_LENGTH ) {
|
|
RtlInitUnicodeString( &logFileString, logFileSize );
|
|
status = RtlUnicodeStringToInteger( &logFileString,
|
|
10,
|
|
&logSize );
|
|
if ( NT_SUCCESS( status ) ) {
|
|
OmpLogFileLimit = logSize;
|
|
}
|
|
} else {
|
|
OmpLogFileLimit = defaultLogSize;
|
|
}
|
|
|
|
status = ERROR_SUCCESS;
|
|
|
|
if ( OmpLogFileLimit == 0 ) {
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// make the file size no bigger than one-eighth the size of the normal log
|
|
// file but no less than 256KB
|
|
//
|
|
if ( OmpLogFileLimit > MAX_FILE_SIZE ) {
|
|
OmpLogFileLimit = MAX_FILE_SIZE;
|
|
}
|
|
OmpLogFileLimit = ( OmpLogFileLimit * ( 1024 * 1024 )) / 8;
|
|
if ( OmpLogFileLimit < ( 256 * 1024 )) {
|
|
OmpLogFileLimit = 256 * 1024;
|
|
}
|
|
|
|
//
|
|
// replace the extension with the object log extension; scanning backwards
|
|
// from the end of the string, find either the first occurance of a slash
|
|
// (fwd or back) or a dot or the front of the string.
|
|
//
|
|
dot = logFileName + logFileNameChars - 1;
|
|
while ( dot != logFileName && *dot != L'.' && *dot != L'/' && *dot != L'\\' ) {
|
|
--dot;
|
|
}
|
|
|
|
if ( dot == logFileName ) {
|
|
//
|
|
// no dots, no slashes; append extension to end
|
|
//
|
|
wcscat( logFileName + logFileNameChars, objectLogExtension );
|
|
}
|
|
else if ( *dot == L'/' || *dot == L'\\' ) {
|
|
//
|
|
// found a slash before a dot; append extension to end
|
|
//
|
|
wcscat( logFileName + logFileNameChars, objectLogExtension );
|
|
}
|
|
else if ( *dot == L'.' ) {
|
|
//
|
|
// found a dot before a slash; make sure that the extension isn't
|
|
// already in use; if so don't log.
|
|
//
|
|
if ( ClRtlStrICmp( dot, objectLogExtension ) != 0 ) {
|
|
wcscpy( dot, objectLogExtension );
|
|
} else {
|
|
goto error_exit;
|
|
}
|
|
} else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[OM] Couldn't determine where to append object log extension. Object logging turned off.\n");
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// create a SD giving only local admins and localsystem full access. DACL
|
|
// is set to protected (P) meaning it is not affected by inheritable ACEs
|
|
// in the parent (cluster directory) object.
|
|
//
|
|
if ( !ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
L"D:P(A;;FA;;;BA)(A;;FA;;;SY)",
|
|
SDDL_REVISION_1,
|
|
&logFileSecurityDesc,
|
|
NULL
|
|
)
|
|
)
|
|
{
|
|
logFileSecurityDesc = NULL;
|
|
}
|
|
|
|
logFileSecurityAttr.nLength = sizeof( logFileSecurityAttr );
|
|
logFileSecurityAttr.lpSecurityDescriptor = logFileSecurityDesc;
|
|
logFileSecurityAttr.bInheritHandle = FALSE;
|
|
|
|
OmpLogFileHandle = CreateFile(logFileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
&logFileSecurityAttr,
|
|
OPEN_ALWAYS,
|
|
0,
|
|
NULL );
|
|
|
|
if ( OmpLogFileHandle == INVALID_HANDLE_VALUE ) {
|
|
status = GetLastError();
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[OM] Open of object log file failed. Error %1!u!\n",
|
|
status);
|
|
goto error_exit;
|
|
} else {
|
|
|
|
//
|
|
// write UTF-8 header to beginning of file and get the offset of the
|
|
// EOF; we never want to reset the start of the file after this point.
|
|
//
|
|
WriteFile( OmpLogFileHandle, UTF8_BOM, sizeof( UTF8_BOM ) - 1, &bytesWritten, NULL );
|
|
|
|
OmpCurrentSessionOffset = SetFilePointer( OmpLogFileHandle, 0, NULL, FILE_END );
|
|
if ( OmpCurrentSessionOffset == INVALID_SET_FILE_POINTER ) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[OM] Unable to get object log end of file position. error %1!u!.\n",
|
|
GetLastError());
|
|
|
|
CloseHandle( OmpLogFileHandle );
|
|
goto error_exit;
|
|
}
|
|
|
|
OmpLogToFile = TRUE;
|
|
OmpProcessId = 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.
|
|
//
|
|
|
|
//
|
|
// For now, set the low water mark to be the current offset. When it
|
|
// is time to wrap, we'll lose everything but the current session.
|
|
//
|
|
// the problem is that we're dealing with UTF8 and we can't just jump
|
|
// in the middle of the file and start looking around (we might hit
|
|
// the 2nd byte of a DBCS sequence). For now, we'll leave
|
|
// OmpLogFileLoWater set to zero. It will get updated when the 1/2 way
|
|
// threshold is crossed.
|
|
//
|
|
OmpLogFileLoWater = OmpCurrentSessionOffset;
|
|
#if 0
|
|
|
|
fileSizeLow = GetFileSize( OmpLogFileHandle, &fileSizeHigh );
|
|
if ( fileSizeLow < ( OmpLogFileLimit / 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 < OmpLogFileLimit ) {
|
|
//
|
|
// case 2; start looking at the 1/2 the current limit to find
|
|
// the starting position
|
|
//
|
|
currentPosition = OmpLogFileLimit / 2;
|
|
} else {
|
|
//
|
|
// case 3: start at current size minus 1/2 limit to find our
|
|
// starting position.
|
|
//
|
|
currentPosition = fileSizeLow - ( OmpLogFileLimit / 2 );
|
|
}
|
|
|
|
//
|
|
// backup from the initial file position, read in a block and look
|
|
// for the start of a session. When we find one, backup to the
|
|
// beginning of that line. Use that as the initial starting
|
|
// position when we finally truncate the file.
|
|
//
|
|
OmpLogFileLoWater = 0;
|
|
currentPosition -= LOGBUF_SIZE;
|
|
|
|
SetFilePointer(OmpLogFileHandle,
|
|
currentPosition,
|
|
&fileSizeHigh,
|
|
FILE_BEGIN);
|
|
|
|
do {
|
|
|
|
if ( ReadFile(OmpLogFileHandle,
|
|
buffer,
|
|
LOGBUF_SIZE - 1,
|
|
&bytesRead,
|
|
NULL ) )
|
|
{
|
|
PCHAR p = buffer;
|
|
PCHAR newp;
|
|
|
|
buffer[ bytesRead ] = NULL;
|
|
while ( *p != 'S' && bytesRead-- != 0 ) {
|
|
newp = CharNextExA( CP_UTF8, p, 0 );
|
|
if ( newp == p ) {
|
|
break;
|
|
}
|
|
p = newp;
|
|
}
|
|
|
|
if ( p != newp ) {
|
|
if ( strchr( p, "START" )) {
|
|
//
|
|
// set pointer to beginning of line
|
|
//
|
|
p = currentLine;
|
|
break;
|
|
}
|
|
} else {
|
|
//
|
|
// not found in this block; read in the next one
|
|
//
|
|
|
|
}
|
|
}
|
|
} while ( TRUE );
|
|
|
|
if ( *p == '\n' ) {
|
|
OmpLogFileLoWater = (DWORD)(currentPosition + ( p - buffer + 1 ));
|
|
}
|
|
|
|
if ( OmpLogFileLoWater == 0 ) {
|
|
//
|
|
// couldn't find any reasonable data. just set it to
|
|
// initial current position.
|
|
//
|
|
OmpLogFileLoWater = currentPosition + LOGBUF_SIZE;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
LocalFree( logFileSecurityDesc );
|
|
|
|
//
|
|
// finally, create the threadq that will handle the IO to the file
|
|
//
|
|
OmpLoggerWorkQueue = ClRtlCreateWorkQueue( 1, THREAD_PRIORITY_BELOW_NORMAL );
|
|
if ( OmpLoggerWorkQueue == NULL ) {
|
|
CloseHandle( OmpLogFileHandle );
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[OM] Unable to logger work queue. error %1!u!.\n",
|
|
GetLastError());
|
|
}
|
|
|
|
error_exit:
|
|
if ( logFileName != logFileBuffer && logFileName != NULL ) {
|
|
LocalFree( logFileName );
|
|
}
|
|
|
|
} // OmpOpenObjectLog
|
|
|
|
VOID
|
|
OmpLogStartRecord(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Description
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
OSVERSIONINFOEXW version;
|
|
BOOL success;
|
|
PWCHAR suiteAbbrev;
|
|
SYSTEMTIME localTime;
|
|
|
|
GetLocalTime( &localTime );
|
|
|
|
version.dwOSVersionInfoSize = sizeof(version);
|
|
success = GetVersionExW((POSVERSIONINFOW)&version);
|
|
|
|
if ( success ) {
|
|
//
|
|
// Log the System version number
|
|
//
|
|
if ( version.wSuiteMask & VER_SUITE_DATACENTER ) {
|
|
suiteAbbrev = L"DTC";
|
|
} else if ( version.wSuiteMask & VER_SUITE_ENTERPRISE ) {
|
|
suiteAbbrev = L"ADS";
|
|
} else if ( version.wSuiteMask & VER_SUITE_EMBEDDEDNT ) {
|
|
suiteAbbrev = L"EMB";
|
|
} else if ( version.wProductType & VER_NT_WORKSTATION ) {
|
|
suiteAbbrev = L"WS";
|
|
} else if ( version.wProductType & VER_NT_DOMAIN_CONTROLLER ) {
|
|
suiteAbbrev = L"DC";
|
|
} else if ( version.wProductType & VER_NT_SERVER ) {
|
|
suiteAbbrev = L"SRV"; // otherwise - some non-descript Server
|
|
} else {
|
|
suiteAbbrev = L"";
|
|
}
|
|
|
|
OmpLogPrint(L"START %1!02d!/%2!02d!/%3!02d!-%4!02d!:%5!02d!:%6!02d!.%7!03d! %8!u! %9!u! "
|
|
L"%10!u! %11!u! %12!u! %13!u! \"%14!ws!\" "
|
|
L"%15!u! %16!u! %17!04X! (%18!ws!) %19!u!\n",
|
|
localTime.wYear,
|
|
localTime.wMonth,
|
|
localTime.wDay,
|
|
localTime.wHour,
|
|
localTime.wMinute,
|
|
localTime.wSecond,
|
|
localTime.wMilliseconds,
|
|
CLUSTER_GET_MAJOR_VERSION( CsMyHighestVersion ),
|
|
CLUSTER_GET_MINOR_VERSION( CsMyHighestVersion ),
|
|
version.dwMajorVersion, // param 10
|
|
version.dwMinorVersion,
|
|
version.dwBuildNumber,
|
|
version.dwPlatformId,
|
|
version.szCSDVersion,
|
|
version.wServicePackMajor, // param 15
|
|
version.wServicePackMinor,
|
|
version.wSuiteMask,
|
|
suiteAbbrev,
|
|
version.wProductType);
|
|
}
|
|
|
|
} // OmpLogStartRecord
|
|
|
|
VOID
|
|
OmpLogStopRecord(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Description
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
OmpLogPrint( L"STOP\n" );
|
|
|
|
} // OmpLogStopRecord
|
|
|
|
|
|
/* end omlog.c */
|