360 lines
9.3 KiB
C
360 lines
9.3 KiB
C
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved
|
|
|
|
/*++
|
|
|
|
Module Name:
|
|
|
|
wheaevt.c
|
|
|
|
Abstract:
|
|
|
|
This file contains routines used to extract WHEA error records from the WHEA
|
|
event log channel.
|
|
|
|
--*/
|
|
|
|
#include "cperhlpp.h"
|
|
|
|
#define WHEA_CHANNEL L"Microsoft-Windows-Kernel-WHEA/Errors"
|
|
#define WHEA_CHANNEL_LEGACY L"Microsoft-Windows-Kernel-WHEA"
|
|
#define WHEA_LOG_QUERY L"*[System/Provider[@Name=\"Microsoft-Windows-Kernel-WHEA\"] and System/EventID=20]"
|
|
|
|
EVT_HANDLE
|
|
CperOpenWheaLogQuery (
|
|
__in_opt PWSTR ComputerName,
|
|
__in_opt PWSTR UserName,
|
|
__in_opt PWSTR Domain,
|
|
__in_opt PWSTR Password,
|
|
__in_opt PWSTR FileName,
|
|
__out EVT_HANDLE *Session
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will initialize an event log query that may be used to
|
|
enumerate any WHEA error records contained in the WHEA event log.
|
|
|
|
Arguments:
|
|
|
|
ComputerName - Supplies an optional computer name for remote queries. This
|
|
should be NULL for the local event log query or if an exported event log
|
|
is to be queried.
|
|
|
|
UserName - Supplies the username to be used to authenticate to the remote
|
|
computer.
|
|
|
|
Domain - Supplies the username to be used to authenticate to the remote
|
|
computer.
|
|
|
|
Password - Supplies the password to be used to authenticate to the remote
|
|
computer.
|
|
|
|
FileName - Supplies an optional filename for an exported event log. This
|
|
should be NULL for a live (local or remote) event log query.
|
|
|
|
Session - Supplies a variable in which a handle to the session is returned,
|
|
but only if the query is for events on a remote computer.
|
|
|
|
Return Value:
|
|
|
|
A handle to the ETW query if successful, NULL otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
DWORD Error;
|
|
DWORD Flags;
|
|
EVT_RPC_LOGIN Login;
|
|
PCWSTR Path;
|
|
EVT_HANDLE QueryHandle;
|
|
EVT_HANDLE SessionHandle;
|
|
|
|
QueryHandle = NULL;
|
|
SessionHandle = NULL;
|
|
Error = ERROR_SUCCESS;
|
|
|
|
//
|
|
// If a computer name is specified, then an event log session to that
|
|
// computer must be opened. It is invalid to specify a remote computer as
|
|
// well as a filename.
|
|
//
|
|
|
|
if (ComputerName != NULL) {
|
|
if (FileName != NULL) {
|
|
Error = ERROR_INVALID_PARAMETER;
|
|
goto OpenWheaLogQueryEnd;
|
|
}
|
|
|
|
RtlZeroMemory(&Login, sizeof(EVT_RPC_LOGIN));
|
|
Login.Server = ComputerName;
|
|
Login.User = UserName;
|
|
Login.Domain = Domain;
|
|
Login.Password = Password;
|
|
Login.Flags = EvtRpcLoginAuthDefault;
|
|
SessionHandle = EvtOpenSession(EvtRpcLogin, &Login, 0, 0);
|
|
if (SessionHandle == NULL) {
|
|
Error = GetLastError();
|
|
goto OpenWheaLogQueryEnd;
|
|
}
|
|
}
|
|
|
|
if (FileName == NULL) {
|
|
Path = WHEA_CHANNEL;
|
|
Flags = EvtQueryChannelPath | EvtQueryForwardDirection;
|
|
|
|
} else {
|
|
Path = (PCWSTR)FileName;
|
|
Flags = EvtQueryFilePath | EvtQueryForwardDirection;
|
|
}
|
|
|
|
//
|
|
// Open the query. If this is not a file query and the open fails, try the
|
|
// legacy log name.
|
|
//
|
|
|
|
QueryHandle = EvtQuery(SessionHandle, Path, WHEA_LOG_QUERY, Flags);
|
|
if (QueryHandle == NULL) {
|
|
Error = GetLastError();
|
|
if (FileName == NULL) {
|
|
Path = WHEA_CHANNEL_LEGACY;
|
|
QueryHandle = EvtQuery(SessionHandle, Path, WHEA_LOG_QUERY, Flags);
|
|
if (QueryHandle == NULL) {
|
|
Error = GetLastError();
|
|
goto OpenWheaLogQueryEnd;
|
|
}
|
|
}
|
|
}
|
|
|
|
*Session = SessionHandle;
|
|
|
|
OpenWheaLogQueryEnd:
|
|
if (QueryHandle == NULL) {
|
|
if (SessionHandle != NULL) {
|
|
EvtClose(SessionHandle);
|
|
}
|
|
|
|
SetLastError(Error);
|
|
}
|
|
|
|
return QueryHandle;
|
|
}
|
|
|
|
__success(return != FALSE)
|
|
BOOL
|
|
CperGetNextWheaLogEntry (
|
|
__in EVT_HANDLE QueryHandle,
|
|
__out_bcount_part_opt(BufferSize, *ReturnedSize) PWHEA_ERROR_RECORD Record,
|
|
__in DWORD BufferSize,
|
|
__out DWORD *ReturnedSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will return the next available WHEA error record logged to the
|
|
WHEA error event log channel.
|
|
|
|
Arguments:
|
|
|
|
QueryHandle - Supplies a handle to the event log query returned by a call to
|
|
CperOpenWheaLogQuery.
|
|
|
|
Buffer - Supplies a pointer to the buffer in which the error record is
|
|
returned. This parameter may be NULL if the buffer size specified is
|
|
zero. In this case, the function will return the required size in the
|
|
returned size field.
|
|
|
|
BufferSize - Supplies the length of the buffer in bytes.
|
|
|
|
ReturnedSize - Supplies a pointer to a variable in which the length of the
|
|
returned error record is returned. If the supplied buffer is too small
|
|
then the required length is returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE upon success, FALSE otherwise. In the case of failure, the error code
|
|
can be obtained by calling GetLastError(). If there are no error records
|
|
remaining, the function will fail with the error code ERROR_NO_MORE_ITEMS.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PVOID Buffer;
|
|
PWHEA_ERROR_RECORD ErrorRecord;
|
|
EVT_HANDLE EventHandle;
|
|
DWORD LastError;
|
|
DWORD PropertyCount;
|
|
DWORD RecordSize;
|
|
EVT_HANDLE RenderContext;
|
|
BOOL Result;
|
|
DWORD Returned;
|
|
PEVT_VARIANT Variant;
|
|
|
|
EventHandle = NULL;
|
|
RenderContext = NULL;
|
|
Buffer = NULL;
|
|
LastError = NO_ERROR;
|
|
|
|
Result = EvtNext(QueryHandle, 1, &EventHandle, (DWORD)-1, 0, &Returned);
|
|
if (Result == FALSE) {
|
|
EventHandle = NULL;
|
|
goto GetNextWheaLogEntryEnd;
|
|
}
|
|
|
|
RenderContext = EvtCreateRenderContext(0, NULL, EvtRenderContextUser);
|
|
if (RenderContext == NULL) {
|
|
Result = FALSE;
|
|
goto GetNextWheaLogEntryEnd;
|
|
}
|
|
|
|
//
|
|
// Make the call to the rendering routine to determine the buffer size
|
|
// required to contain the rendered event. This call should fail with an
|
|
// insufficient buffer error. Anything else (including success) is an
|
|
// immediate failure.
|
|
//
|
|
|
|
Result = EvtRender(RenderContext,
|
|
EventHandle,
|
|
EvtRenderEventValues,
|
|
0,
|
|
NULL,
|
|
&Returned,
|
|
&PropertyCount);
|
|
|
|
if (Result != FALSE) {
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
Result = FALSE;
|
|
goto GetNextWheaLogEntryEnd;
|
|
}
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
goto GetNextWheaLogEntryEnd;
|
|
}
|
|
|
|
//
|
|
// Allocate the buffer to contain the rendered event log entry and render
|
|
// the entry.
|
|
//
|
|
|
|
Buffer = HeapAlloc(GetProcessHeap(), 0, Returned);
|
|
if (Buffer == NULL) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
Result = FALSE;
|
|
goto GetNextWheaLogEntryEnd;
|
|
}
|
|
|
|
Result = EvtRender(RenderContext,
|
|
EventHandle,
|
|
EvtRenderEventValues,
|
|
Returned,
|
|
Buffer,
|
|
&Returned,
|
|
&PropertyCount);
|
|
|
|
if (Result == FALSE) {
|
|
goto GetNextWheaLogEntryEnd;
|
|
}
|
|
|
|
//
|
|
// This particular event has 2 properties. Anything less than this and
|
|
// return failure.
|
|
//
|
|
|
|
if (PropertyCount < 2) {
|
|
Result = FALSE;
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto GetNextWheaLogEntryEnd;
|
|
}
|
|
|
|
//
|
|
// The first property is the error record length.
|
|
//
|
|
|
|
Variant = (PEVT_VARIANT)Buffer;
|
|
if (Variant->Type != EvtVarTypeUInt32) {
|
|
Result = FALSE;
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto GetNextWheaLogEntryEnd;
|
|
}
|
|
|
|
RecordSize = (DWORD)Variant->UInt32Val;
|
|
|
|
//
|
|
// The second property is the error record itself.
|
|
//
|
|
|
|
Variant += 1;
|
|
if (Variant->Type != EvtVarTypeBinary) {
|
|
Result = FALSE;
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto GetNextWheaLogEntryEnd;
|
|
}
|
|
|
|
ErrorRecord = (PWHEA_ERROR_RECORD)Variant->BinaryVal;
|
|
|
|
//
|
|
// Ensure that the record length property and the length field of the error
|
|
// record header are the same.
|
|
//
|
|
|
|
if (ErrorRecord->Header.Length != RecordSize) {
|
|
Result = FALSE;
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
goto GetNextWheaLogEntryEnd;
|
|
}
|
|
|
|
//
|
|
// If the supplied buffer can contain the error record, then return it. If
|
|
// not then return the appropriate error code as well as the buffer size
|
|
// required to contain the error record. The query should also be
|
|
// repositioned so that when the caller calls again to get the entry it will
|
|
// retry the same one.
|
|
//
|
|
|
|
*ReturnedSize = RecordSize;
|
|
if (BufferSize >= RecordSize) {
|
|
RtlCopyMemory(Record, ErrorRecord, RecordSize);
|
|
Result = TRUE;
|
|
|
|
} else {
|
|
EvtSeek(QueryHandle, -1, NULL, 0, EvtSeekRelativeToCurrent);
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
Result = FALSE;
|
|
}
|
|
|
|
GetNextWheaLogEntryEnd:
|
|
if (Result == FALSE) {
|
|
LastError = GetLastError();
|
|
}
|
|
|
|
if (EventHandle != NULL) {
|
|
EvtClose(EventHandle);
|
|
}
|
|
|
|
if (RenderContext != NULL) {
|
|
EvtClose(RenderContext);
|
|
}
|
|
|
|
if (Buffer != NULL) {
|
|
HeapFree(GetProcessHeap(), 0, Buffer);
|
|
}
|
|
|
|
if (Result == FALSE) {
|
|
SetLastError(LastError);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|