|
|
/*++
Copyright (c) 1998-1999 Microsoft Corporation
Module Name:
globalog.c
Abstract:
The global logger, which is started only by registry settings. Will start at boot.
Author:
Jee Fung Pang (jeepang) 03-Nov-1998
Revision History:
--*/
#ifndef MEMPHIS
#include "ntos.h"
#include <evntrace.h>
#include "wmikmp.h"
#include "tracep.h"
#define MAX_REGKEYS 10
#define MAX_ENABLE_FLAGS 10
#define TRACE_VERSION_MAJOR 1
#define TRACE_VERSION_MINOR 0
#define DOSDEVICES L"\\DosDevices\\"
#define UNCDEVICES L"\\??\\UNC"
#define DEFAULT_GLOBAL_LOGFILE_ROOT L"%SystemRoot%"
#define DEFAULT_GLOBAL_DIRECTORY L"\\System32\\LogFiles\\WMI"
#define DEFAULT_GLOBAL_LOGFILE L"trace.log"
//
// NOTE: Need a trailing NULL entry so that RtlQueryRegistryValues()
// knows when to stop
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, WmipStartGlobalLogger)
#pragma alloc_text(PAGE, WmipQueryGLRegistryRoutine)
#pragma alloc_text(PAGE, WmipAddLogHeader)
#pragma alloc_text(PAGE, WmipDelayCreate)
#pragma alloc_text(PAGE, WmipCreateDirectoryFile)
#pragma alloc_text(PAGE, WmipCreateNtFileName)
#endif
extern HANDLE WmipPageLockHandle; //
// NOTE: If we are going to function earlier in boot, we need to see
// if the creation routines and logger routines can run at all while in
// boot path and being pagable
//
VOID WmipStartGlobalLogger( ) /*++
Routine Description:
This routine will check for registry entries to see if the global needs to be started at boot time.
Arguments:
None
Return Value:
--*/ { struct _LOGGER_INFO { WMI_LOGGER_INFORMATION LoggerInfo; ULONG EnableFlags[MAX_ENABLE_FLAGS]; } GLog; RTL_QUERY_REGISTRY_TABLE QueryRegistryTable[MAX_REGKEYS]; NTSTATUS status; ULONG StartRequested = 0; WCHAR NullString = UNICODE_NULL;
WmipPageLockHandle = MmLockPagableCodeSection((PVOID)WmipReserveTraceBuffer); MmUnlockPagableImageSection(WmipPageLockHandle); ExInitializeFastMutex(&WmipTraceFastMutex);
RtlZeroMemory(&GLog, sizeof(GLog));
GLog.LoggerInfo.Wnode.Flags = WNODE_FLAG_TRACED_GUID; GLog.LoggerInfo.MinimumBuffers = (ULONG) KeNumberProcessors + 1; GLog.LoggerInfo.MaximumBuffers = GLog.LoggerInfo.MinimumBuffers + 25; GLog.LoggerInfo.BufferSize = PAGE_SIZE / 1024; GLog.LoggerInfo.Wnode.BufferSize = sizeof(WMI_LOGGER_INFORMATION); GLog.LoggerInfo.Wnode.Guid = GlobalLoggerGuid; GLog.LoggerInfo.LogFileMode = EVENT_TRACE_DELAY_OPEN_FILE_MODE; RtlInitUnicodeString(&GLog.LoggerInfo.LoggerName, L"GlobalLogger");
RtlZeroMemory(QueryRegistryTable, sizeof(RTL_QUERY_REGISTRY_TABLE) * MAX_REGKEYS);
QueryRegistryTable[0].QueryRoutine = WmipQueryGLRegistryRoutine; QueryRegistryTable[0].EntryContext = (PVOID) &StartRequested; QueryRegistryTable[0].Name = L"Start"; QueryRegistryTable[0].DefaultType = REG_DWORD;
QueryRegistryTable[1].QueryRoutine = WmipQueryGLRegistryRoutine; QueryRegistryTable[1].EntryContext = (PVOID) &GLog.LoggerInfo.BufferSize; QueryRegistryTable[1].Name = L"BufferSize"; QueryRegistryTable[1].DefaultType = REG_DWORD;
QueryRegistryTable[2].QueryRoutine = WmipQueryGLRegistryRoutine; QueryRegistryTable[2].EntryContext = (PVOID)&GLog.LoggerInfo.MinimumBuffers; QueryRegistryTable[2].Name = L"MinimumBuffers"; QueryRegistryTable[2].DefaultType = REG_DWORD;
QueryRegistryTable[3].QueryRoutine = WmipQueryGLRegistryRoutine; QueryRegistryTable[3].EntryContext = (PVOID) &GLog.LoggerInfo.FlushTimer; QueryRegistryTable[3].Name = L"FlushTimer"; QueryRegistryTable[3].DefaultType = REG_DWORD;
QueryRegistryTable[4].QueryRoutine = WmipQueryGLRegistryRoutine; QueryRegistryTable[4].EntryContext = (PVOID)&GLog.LoggerInfo.MaximumBuffers; QueryRegistryTable[4].Name = L"MaximumBuffers"; QueryRegistryTable[4].DefaultType = REG_DWORD;
QueryRegistryTable[5].QueryRoutine = WmipQueryGLRegistryRoutine; QueryRegistryTable[5].EntryContext = (PVOID) &GLog.LoggerInfo.LogFileName; QueryRegistryTable[5].Name = L"FileName"; QueryRegistryTable[5].DefaultType = REG_SZ; QueryRegistryTable[5].DefaultData = &NullString;
QueryRegistryTable[6].QueryRoutine = WmipQueryGLRegistryRoutine; QueryRegistryTable[6].EntryContext = (PVOID) &GLog.EnableFlags[0]; QueryRegistryTable[6].Name = L"EnableKernelFlags"; QueryRegistryTable[6].DefaultType = REG_BINARY;
QueryRegistryTable[7].QueryRoutine = WmipQueryGLRegistryRoutine; QueryRegistryTable[7].EntryContext = (PVOID)&GLog.LoggerInfo.Wnode.ClientContext; QueryRegistryTable[7].Name = L"ClockType"; QueryRegistryTable[7].DefaultType = REG_DWORD;
status = RtlQueryRegistryValues( RTL_REGISTRY_CONTROL, L"WMI\\GlobalLogger", QueryRegistryTable, NULL, NULL);
if (NT_SUCCESS(status) && (StartRequested != 0)) { if (GLog.EnableFlags[0] != 0) { SHORT Length; for (Length=MAX_ENABLE_FLAGS-1; Length>=0; Length--) { if (GLog.EnableFlags[Length] != 0) break; } if (Length >= 0) { PTRACE_ENABLE_FLAG_EXTENSION FlagExt; Length++; // Index is 1 less!
FlagExt = (PTRACE_ENABLE_FLAG_EXTENSION) &GLog.LoggerInfo.EnableFlags; GLog.LoggerInfo.EnableFlags = EVENT_TRACE_FLAG_EXTENSION; FlagExt->Length = (UCHAR) Length; FlagExt->Offset = (UCHAR) GLog.LoggerInfo.Wnode.BufferSize; GLog.LoggerInfo.Wnode.BufferSize += (ULONG) (Length * sizeof(ULONG)); } } if (GLog.LoggerInfo.LogFileName.Buffer == NULL) { RtlCreateUnicodeString( &GLog.LoggerInfo.LogFileName, DEFAULT_GLOBAL_LOGFILE_ROOT); // Use ROOT as indicator
if (GLog.LoggerInfo.LogFileName.Buffer == NULL) status = STATUS_NO_MEMORY; else status = STATUS_SUCCESS; } if (NT_SUCCESS(status)) { status = WmipStartLogger(&GLog.LoggerInfo); } } if (GLog.LoggerInfo.LogFileName.Buffer) { RtlFreeUnicodeString(&GLog.LoggerInfo.LogFileName); } }
NTSTATUS WmipQueryGLRegistryRoutine( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) /*++
Routine Description:
Registry query values callback routine for reading SDs for guids
Arguments:
ValueName - the name of the value
ValueType - the type of the value
ValueData - the data in the value (unicode string data)
ValueLength - the number of bytes in the value data
Context - Not used
EntryContext - Pointer to PSECURITY_DESCRIPTOR to store a pointer to store the security descriptor read from the registry value
Return Value:
NT Status code
--*/ { NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE(); UNREFERENCED_PARAMETER(ValueName); UNREFERENCED_PARAMETER(Context);
if ( (ValueData != NULL) && (ValueLength > 0) && (EntryContext != NULL) ){ if (ValueType == REG_DWORD) { if ((ValueLength >= sizeof(ULONG)) && (ValueData != NULL)) { *((PULONG)EntryContext) = *((PULONG)ValueData); } } else if (ValueType == REG_SZ) { if (ValueLength > sizeof(UNICODE_NULL)) { status = RtlCreateUnicodeString( (PUNICODE_STRING) EntryContext, (PCWSTR) ValueData); } } else if (ValueType == REG_BINARY) { if ((ValueLength >= sizeof(ULONG)) && (ValueData != NULL)) { RtlMoveMemory(EntryContext, ValueData, ValueLength); } } } return status; }
NTSTATUS WmipAddLogHeader( IN PWMI_LOGGER_CONTEXT LoggerContext, IN OUT PWMI_BUFFER_HEADER Buffer ) /*++
Routine Description:
Add a standard logfile header in kernel moder. To make sure the first buffer of the log file contains the file header, we pop a buffer from the free list, write the header, and flush the buffer right away.
Arguments:
LoggerContext - The logger context
Return Value:
NT Status code
--*/ { PTRACE_LOGFILE_HEADER LogfileHeader; USHORT HeaderSize; PSYSTEM_TRACE_HEADER EventTrace; PSINGLE_LIST_ENTRY SingleListEntry; PETHREAD Thread; NTSTATUS Status = STATUS_SUCCESS; ULONG BufferProvided = (Buffer != NULL); ULONG LocalBuffer = FALSE;
if (LoggerContext == NULL) { return STATUS_INVALID_PARAMETER; }
HeaderSize = sizeof(TRACE_LOGFILE_HEADER) + LoggerContext->LoggerName.Length + sizeof(UNICODE_NULL) + LoggerContext->LogFileName.Length + sizeof(UNICODE_NULL); if (LoggerContext->BufferSize < (HeaderSize - sizeof(WMI_BUFFER_HEADER))) { return STATUS_NO_MEMORY; }
//
// Pop a buffer from Free List
//
if (!BufferProvided) { Buffer = WmipGetFreeBuffer(LoggerContext);
if (Buffer == NULL) { Buffer = ExAllocatePoolWithTag(PagedPool, LoggerContext->BufferSize, TRACEPOOLTAG); if (Buffer == NULL) {
//
// No buffer available.
//
return STATUS_NO_MEMORY; } LocalBuffer = TRUE;
Buffer->Flags = 1; Buffer->SavedOffset = 0; Buffer->CurrentOffset = sizeof(WMI_BUFFER_HEADER); Buffer->Wnode.ClientContext = 0; Buffer->LoggerContext = LoggerContext;
Buffer->State.Free = 0; Buffer->State.InUse = 1; KeQuerySystemTime(&Buffer->TimeStamp); } }
//
// Fill in the Header Info.
//
Thread = PsGetCurrentThread(); EventTrace = (PSYSTEM_TRACE_HEADER) (Buffer+1); EventTrace->Packet.Group = (UCHAR) EVENT_TRACE_GROUP_HEADER >> 8; EventTrace->Packet.Type = EVENT_TRACE_TYPE_INFO; EventTrace->Packet.Size = HeaderSize + sizeof(SYSTEM_TRACE_HEADER); EventTrace->Marker = SYSTEM_TRACE_MARKER; EventTrace->ThreadId = HandleToUlong(Thread->Cid.UniqueThread); EventTrace->KernelTime = Thread->Tcb.KernelTime; EventTrace->UserTime = Thread->Tcb.UserTime; EventTrace->SystemTime = LoggerContext->ReferenceTimeStamp;
LogfileHeader = (PTRACE_LOGFILE_HEADER) (EventTrace+1); RtlZeroMemory(LogfileHeader, HeaderSize); LogfileHeader->StartTime = LoggerContext->ReferenceSystemTime;
LogfileHeader->BufferSize = LoggerContext->BufferSize; LogfileHeader->VersionDetail.MajorVersion = (UCHAR) NtMajorVersion; LogfileHeader->VersionDetail.MinorVersion = (UCHAR) NtMinorVersion; LogfileHeader->VersionDetail.SubVersion = TRACE_VERSION_MAJOR; LogfileHeader->VersionDetail.SubMinorVersion = TRACE_VERSION_MINOR; LogfileHeader->ProviderVersion = NtBuildNumber;
LogfileHeader->StartBuffers = 1; LogfileHeader->BootTime = KeBootTime; LogfileHeader->LogFileMode = LoggerContext->LoggerMode & (~(EVENT_TRACE_REAL_TIME_MODE)); LogfileHeader->NumberOfProcessors = KeNumberProcessors; LogfileHeader->MaximumFileSize = LoggerContext->MaximumFileSize; KeQueryPerformanceCounter(&LogfileHeader->PerfFreq);
//
// ReservedFlags to indicate using Perf Clock
//
LogfileHeader->ReservedFlags = LoggerContext->UsePerfClock;
LogfileHeader->TimerResolution = KeMaximumIncrement;
LogfileHeader->LoggerName = (PWCHAR) ( (PUCHAR) LogfileHeader + sizeof(TRACE_LOGFILE_HEADER) ); LogfileHeader->LogFileName = (PWCHAR) ( (PUCHAR)LogfileHeader->LoggerName + LoggerContext->LoggerName.Length + sizeof(UNICODE_NULL));
RtlCopyMemory(LogfileHeader->LoggerName, LoggerContext->LoggerName.Buffer, LoggerContext->LoggerName.Length + sizeof(UNICODE_NULL)); RtlCopyMemory(LogfileHeader->LogFileName, LoggerContext->LogFileName.Buffer, LoggerContext->LogFileName.Length + sizeof(UNICODE_NULL)); RtlQueryTimeZoneInformation(&LogfileHeader->TimeZone); LogfileHeader->PointerSize = sizeof(PVOID);
//
// Adjust the Offset;
//
Buffer->CurrentOffset += ALIGN_TO_POWER2(sizeof(SYSTEM_TRACE_HEADER) + HeaderSize, WmiTraceAlignment);
if (BufferProvided) return Status; //
// The buffer is prepared properly. Flush it so it can be written out to disk.
//
Status = WmipFlushBuffer(LoggerContext, Buffer);
if (LocalBuffer && (Buffer != NULL)) { ExFreePool(Buffer); return Status; }
InterlockedPushEntrySList(&LoggerContext->FreeList, (PSINGLE_LIST_ENTRY) &Buffer->SlistEntry); InterlockedIncrement(&LoggerContext->BuffersAvailable); InterlockedDecrement(&LoggerContext->BuffersInUse); TraceDebug((2, "WmipAddLogHeader: Boot %I64u Current %I64u Difference %I64u\n", KeBootTime, EventTrace->SystemTime, EventTrace->SystemTime.QuadPart - KeBootTime.QuadPart));
return Status; }
NTSTATUS WmipDelayCreate( OUT PHANDLE FileHandle, IN OUT PUNICODE_STRING FileName, IN ULONG Append ) /*++
Routine Description:
This is called by the global logger to actually open the logfile when the first buffer needs to flush (instead of when the logger started)
Arguments:
LoggerHandle The handle to the logfile to be returned FileName The logfile name. If the default was chosen, we will returned the actual pathname in %systemroot%
Return Value:
NT Status code
--*/ { PWCHAR Buffer; PWCHAR strBuffer = NULL; ULONG DefaultFile, Length; UNICODE_STRING LogFileName; NTSTATUS Status;
if (FileName == NULL) return STATUS_INVALID_PARAMETER;
RtlInitUnicodeString(&LogFileName, DEFAULT_GLOBAL_LOGFILE_ROOT); DefaultFile = (RtlCompareUnicodeString(FileName, &LogFileName, TRUE) == 0);
if (DefaultFile) { //
// Try creating the file first
//
Length = (ULONG) ( NtSystemRoot.Length + sizeof(WCHAR) * (wcslen(DEFAULT_GLOBAL_DIRECTORY) + wcslen(DEFAULT_GLOBAL_LOGFILE) + 1) + sizeof(UNICODE_NULL)); strBuffer = (PWCHAR) ExAllocatePoolWithTag( PagedPool, Length, TRACEPOOLTAG); if (strBuffer == NULL) return STATUS_NO_MEMORY;
swprintf(strBuffer, L"%ws%ws\\%ws", NtSystemRoot.Buffer, DEFAULT_GLOBAL_DIRECTORY, DEFAULT_GLOBAL_LOGFILE);
Status = WmipCreateNtFileName(strBuffer, & Buffer); if (!NT_SUCCESS(Status)) { ExFreePool(strBuffer); return Status; }
Status = WmipCreateDirectoryFile(Buffer, FALSE, FileHandle, Append); if (!NT_SUCCESS(Status)) { ULONG DirLen; //
// Probably directory does not exist, so try and create it
//
DirLen = (ULONG) (wcslen(Buffer)-wcslen(DEFAULT_GLOBAL_LOGFILE)) - 5; Buffer[DirLen] = UNICODE_NULL; Status = WmipCreateDirectoryFile(Buffer, TRUE, NULL, Append); if (NT_SUCCESS(Status)) { Buffer[DirLen] = L'\\'; DirLen += 4; Buffer[DirLen] = UNICODE_NULL; Status = WmipCreateDirectoryFile(Buffer, TRUE, NULL, Append); Buffer[DirLen] = L'\\'; } if (NT_SUCCESS(Status)) { Status = WmipCreateDirectoryFile(Buffer, FALSE, FileHandle, Append); } } // Make sure that directory is there first
if (NT_SUCCESS(Status)) { if (FileName->Buffer != NULL) { RtlFreeUnicodeString(FileName); } RtlInitUnicodeString(FileName, strBuffer); if (FileName->MaximumLength < Length) FileName->MaximumLength = (USHORT) Length; } } else { Status = WmipCreateNtFileName(FileName->Buffer, & Buffer); if (NT_SUCCESS(Status)) { Status = WmipCreateDirectoryFile(Buffer, FALSE, FileHandle, Append); } }
if (Buffer != NULL) { ExFreePool(Buffer); } return Status; }
NTSTATUS WmipCreateDirectoryFile( IN PWCHAR DirFileName, IN ULONG IsDirectory, OUT PHANDLE FileHandle, IN ULONG Append ) { OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatus; UNICODE_STRING LogDirName; HANDLE DirHandle = NULL; NTSTATUS Status; ULONG CreateDisposition;
RtlInitUnicodeString(&LogDirName, DirFileName); InitializeObjectAttributes( &ObjectAttributes, &LogDirName, OBJ_CASE_INSENSITIVE, NULL, NULL);
if (IsDirectory) { CreateDisposition = FILE_OPEN_IF; } else if (Append) { CreateDisposition = FILE_OPEN_IF; } else { CreateDisposition = FILE_OVERWRITE_IF; }
Status = ZwCreateFile( &DirHandle, FILE_GENERIC_READ | SYNCHRONIZE | (IsDirectory ? FILE_GENERIC_WRITE : FILE_WRITE_DATA), &ObjectAttributes, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, CreateDisposition, FILE_SYNCHRONOUS_IO_NONALERT | (IsDirectory ? FILE_DIRECTORY_FILE : FILE_NO_INTERMEDIATE_BUFFERING), NULL, 0);
TraceDebug((2, "WmipCreateDirectoryFile: Create %ws Mode: %x status: %x\n", DirFileName, Append, Status));
if (NT_SUCCESS(Status) && IsDirectory && (DirHandle != NULL)) { ZwClose(DirHandle); if (FileHandle) *FileHandle = NULL; } else { if (FileHandle) *FileHandle = DirHandle; }
return Status; }
NTSTATUS WmipCreateNtFileName( IN PWCHAR strFileName, OUT PWCHAR * strNtFileName ) { PWCHAR NtFileName; ULONG lenFileName;
if (strFileName == NULL) { * strNtFileName = NULL; return STATUS_INVALID_PARAMETER; }
lenFileName = sizeof(UNICODE_NULL) + (ULONG) (sizeof(WCHAR) * wcslen(strFileName)); if ((strFileName[0] == L'\\') && (strFileName[1] == L'\\')) { lenFileName += (ULONG) (wcslen(UNCDEVICES) * sizeof(WCHAR)); } else { lenFileName += (ULONG) (wcslen(DOSDEVICES) * sizeof(WCHAR)); } NtFileName = (PWCHAR) ExAllocatePoolWithTag( PagedPool, lenFileName, TRACEPOOLTAG); if (NtFileName == NULL) { * strNtFileName = NULL; return STATUS_NO_MEMORY; }
if ((strFileName[0] == L'\\') && (strFileName[1] == L'\\')) { swprintf(NtFileName, L"%ws%ws", UNCDEVICES, & (strFileName[1])); } else { swprintf(NtFileName, L"%ws%ws", DOSDEVICES, strFileName); } * strNtFileName = NtFileName;
return STATUS_SUCCESS; } #endif // !MEMPHIS
|