windows-nt/Source/XPSP1/NT/base/ntos/wmi/globalog.c
2020-09-26 16:20:57 +08:00

627 lines
19 KiB
C

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