Windows2000/private/eventlog/server/elfutil.c
2020-09-30 17:12:32 +02:00

1654 lines
32 KiB
C

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
ELFUTIL.C
Abstract:
This file contains all the utility routines for the Eventlog service.
Author:
Rajen Shah (rajens) 16-Jul-1991
Revision History:
--*/
// INCLUDES
#include <eventp.h>
#include <lmalert.h>
#include <string.h>
#include <stdlib.h>
PLOGMODULE
FindModuleStrucFromAtom(
ATOM Atom
)
/*++
Routine Description:
This routine scans the list of module structures and finds the one
that matches the module atom.
Arguments:
Atom contains the atom matching the module name.
Return Value:
A pointer to the log module structure is returned.
NULL if no matching atom is found.
Note:
--*/
{
PLOGMODULE ModuleStruc;
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
ModuleStruc = CONTAINING_RECORD(
LogModuleHead.Flink,
LOGMODULE,
ModuleList
);
while ((ModuleStruc->ModuleList.Flink != &LogModuleHead)
&&
(ModuleStruc->ModuleAtom != Atom)) {
ModuleStruc = CONTAINING_RECORD(
ModuleStruc->ModuleList.Flink,
LOGMODULE,
ModuleList
);
}
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
return (ModuleStruc->ModuleAtom == Atom ? ModuleStruc : NULL);
}
PLOGMODULE
GetModuleStruc(
PUNICODE_STRING ModuleName
)
/*++
Routine Description:
This routine returns a pointer to the log module structure for the
module specified in ModuleName. If none exists, the default structure
for application is returned.
Arguments:
ModuleName contains the name of the module.
Return Value:
A pointer to the log module structure is returned.
Note:
--*/
{
NTSTATUS Status;
ATOM ModuleAtom;
ANSI_STRING ModuleNameA;
PLOGMODULE pModule;
ElfDbgPrint(("[ELF] GetModuleStruc: "));
Status = RtlUnicodeStringToAnsiString(
&ModuleNameA,
ModuleName,
TRUE
);
if (!NT_SUCCESS(Status)) {
// Not much else we can do here...
ElfDbgPrint((" GetModuleStruc: RtlUnicodeStringToAnsiString FAILED!\n"));
return (ElfDefaultLogModule);
}
// Guarantee that it's NULL terminated
ModuleNameA.Buffer[ModuleNameA.Length] = '\0';
ElfDbgPrint((" Module: %Z ", &ModuleNameA));
ModuleAtom = FindAtomA(ModuleNameA.Buffer);
RtlFreeAnsiString(&ModuleNameA);
if (ModuleAtom == (ATOM)0) {
ElfDbgPrint((" No atom found. Defaulting to APPLICATION.\n"));
return (ElfDefaultLogModule);
}
ElfDbgPrint((" Atom = %d \n", ModuleAtom));
pModule = FindModuleStrucFromAtom(ModuleAtom);
return (pModule != NULL ? pModule : ElfDefaultLogModule);
}
VOID
UnlinkContextHandle(
IELF_HANDLE LogHandle
)
/*++
Routine Description:
This routine unlinks the LogHandle specified from the linked list of
context handles.
In order to protect against multiple thread/process access to the
list at the same time, we use a critical section.
Arguments:
LogHandle points to a context handle structure.
Return Value:
NONE
Note:
--*/
{
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
// Remove this entry
RemoveEntryList(&LogHandle->Next);
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
}
VOID
LinkContextHandle(
IELF_HANDLE LogHandle
)
/*++
Routine Description:
This routine links the LogHandle specified into the linked list of
context handles.
In order to protect against multiple thread/process access to the
list at the same time, we use a critical section.
Arguments:
LogHandle points to a context handle structure.
Return Value:
NONE
Note:
--*/
{
ASSERT(LogHandle->Signature == ELF_CONTEXTHANDLE_SIGN);
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
// Place structure at the beginning of the list.
InsertHeadList(&LogHandleListHead, &LogHandle->Next);
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
}
VOID
UnlinkQueuedEvent(
PELF_QUEUED_EVENT QueuedEvent
)
/*++
Routine Description:
This routine unlinks the QueuedEvent specified from the linked list of
QueuedEvents.
In order to protect against multiple thread/process access to the
list at the same time, we use a critical section.
Arguments:
QueuedEvent - The request to remove from the linked list
Return Value:
NONE
Note:
--*/
{
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
// Remove this entry
RemoveEntryList(&QueuedEvent->Next);
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
}
VOID
LinkQueuedEvent(
PELF_QUEUED_EVENT QueuedEvent
)
/*++
Routine Description:
This routine links the QueuedEvent specified into the linked list of
QueuedEvents.
In order to protect against multiple thread/process access to the
list at the same time, we use a critical section.
Arguments:
QueuedEvent - The request to add from the linked list
Return Value:
NONE
Note:
--*/
{
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
// Place structure at the beginning of the list.
InsertHeadList(&QueuedEventListHead, &QueuedEvent->Next);
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
}
DWORD
WINAPI
ElfpSendMessage(
LPVOID UnUsed
)
/*++
Routine Description:
This routines just uses MessageBox to pop up a message.
This is it's own routine so we can spin a thread to do this, in case the
user doesn't hit the OK button for a while.
Arguments:
NONE
Return Value:
NONE
Note:
--*/
{
PVOID MessageBuffer;
HANDLE hLibrary;
LPWSTR* StringPointers;
DWORD i;
PELF_QUEUED_EVENT QueuedEvent;
PELF_QUEUED_EVENT FlushEvent;
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
// First get a handle to the message file used for the message text
hLibrary = LoadLibraryEx(L"NETMSG.DLL",
NULL,
LOAD_LIBRARY_AS_DATAFILE);
// Walk the linked list and process each element
QueuedEvent = CONTAINING_RECORD(
QueuedMessageListHead.Flink,
struct _ELF_QUEUED_EVENT,
Next
);
while (QueuedEvent->Next.Flink != QueuedMessageListHead.Flink) {
ASSERT(QueuedEvent->Type == Message);
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
// Build the array of pointers to the insertion strings
StringPointers = (LPWSTR*)ElfpAllocateBuffer(
QueuedEvent->Event.Message.NumberOfStrings * sizeof(LPWSTR));
if (StringPointers && hLibrary) {
// Build the array of pointers to the insertion string(s)
if (QueuedEvent->Event.Message.NumberOfStrings) {
StringPointers[0] = (LPWSTR)
((PBYTE)&QueuedEvent->Event.Message +
sizeof(ELF_MESSAGE_RECORD));
for (i = 1; i < QueuedEvent->Event.Message.NumberOfStrings;
i++) {
StringPointers[i] = StringPointers[i - 1] +
wcslen(StringPointers[i - 1]) + 1;
}
}
// Call FormatMessage to build the message
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_ARGUMENT_ARRAY |
FORMAT_MESSAGE_FROM_HMODULE,
hLibrary,
QueuedEvent->Event.Message.MessageId,
0, // Language ID defaulted
(LPWSTR)&MessageBuffer,
0, // Is this ignored if allocate_buffer?
(va_list*)StringPointers)) {
// Now actually display it
MessageBoxW(NULL, (LPWSTR)MessageBuffer, GlobalMessageBoxTitle,
MB_OK | MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_SERVICE_NOTIFICATION);
}
ElfpFreeBuffer(StringPointers);
}
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
// Move to the next one, saving this one to delete it
FlushEvent = QueuedEvent;
QueuedEvent = CONTAINING_RECORD(
QueuedEvent->Next.Flink,
struct _ELF_QUEUED_EVENT,
Next
);
// Now remove this from the queue and free it if we successfully
// processed it
RemoveEntryList(&FlushEvent->Next);
}
FreeLibrary(hLibrary);
MBThreadHandle = NULL;
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
return(0);
}
VOID
LinkQueuedMessage(
PELF_QUEUED_EVENT QueuedEvent
)
/*++
Routine Description:
This routine links the QueuedEvent specified into the linked list of
QueuedMessages. If there's not already a messagebox thread running,
it starts one.
In order to protect against multiple thread/process access to the
list at the same time, we use a critical section.
Arguments:
QueuedEvent - The request to add from the linked list
Return Value:
NONE
Note:
--*/
{
DWORD ThreadId;
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
// Place structure at the end of the list.
InsertTailList(&QueuedMessageListHead, &QueuedEvent->Next);
if (!MBThreadHandle) {
// Since the user can just let this sit on their screen,
// spin a thread for this
MBThreadHandle = CreateThread(NULL, // lpThreadAttributes
4096, // dwStackSize
ElfpSendMessage, // lpStartAddress
NULL, // lpParameter
0L, // dwCreationFlags
&ThreadId); // lpThreadId
}
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&QueuedMessageCritSec);
}
VOID
NotifyChange(
PLOGFILE pLogFile
)
/*++
Routine Description:
This routine runs the list of events that are registered with
ElfChangeNotify to be notified when a log has changed, and pulses
the event.
In order to protect against multiple thread/process access to the
list at the same time, we use an exclusive resource.
Arguments:
LogHandle points to a context handle structure.
Return Value:
NONE
Note:
--*/
{
// How frequently will I try to pulse the events? How about every
// 5 seconds
#define MINIMUM_PULSE_TIME 5
PNOTIFIEE Notifiee;
LARGE_INTEGER Time;
ULONG CurrentTime;
NTSTATUS Status;
// Get exclusive access to the log file. This will ensure no one
// else is accessing the file.
RtlAcquireResourceExclusive(
&pLogFile->Resource,
TRUE); // Wait until available
// See if we've done this in the last MINIMUM_PULSE_TIME seconds
Status = NtQuerySystemTime(&Time);
if (NT_SUCCESS(Status)) {
RtlTimeToSecondsSince1970(&Time, &CurrentTime);
CurrentTime = CurrentTime - MINIMUM_PULSE_TIME;
if (CurrentTime > pLogFile->ulLastPulseTime) {
// Remember that we pulsed
pLogFile->ulLastPulseTime = CurrentTime;
// Walk the linked list and and pulse any events
Notifiee = CONTAINING_RECORD(
pLogFile->Notifiees.Flink,
struct _NOTIFIEE,
Next
);
while (Notifiee->Next.Flink != pLogFile->Notifiees.Flink) {
// Pulse each event as we get to it.
NtPulseEvent(Notifiee->Event, NULL);
Notifiee = CONTAINING_RECORD(
Notifiee->Next.Flink,
struct _NOTIFIEE,
Next);
}
}
}
// Free the resource
RtlReleaseResource(&pLogFile->Resource);
}
VOID
WriteQueuedEvents(
)
/*++
Routine Description:
This routine runs the list of queued events and writes them.
In order to protect against multiple thread/process access to the
list at the same time, we use an exclusive resource.
Arguments:
NONE
Return Value:
NONE
Note:
--*/
{
PELF_QUEUED_EVENT QueuedEvent;
PELF_QUEUED_EVENT FlushEvent;
BOOLEAN bFlushEvent;
LARGE_INTEGER Time;
ULONG CurrentTime;
static ULONG LastAlertTried = 0;
static BOOLEAN LastAlertFailed = FALSE;
// Lock the linked list, you must get the System Log File Resource
// first, it is the higher level lock
RtlAcquireResourceExclusive(
&ElfModule->LogFile->Resource,
TRUE); // Wait until available
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
// Walk the linked list and process each element
QueuedEvent = CONTAINING_RECORD(
QueuedEventListHead.Flink,
struct _ELF_QUEUED_EVENT,
Next);
while (QueuedEvent->Next.Flink != QueuedEventListHead.Flink) {
// Default is to flush the event after processing
bFlushEvent = TRUE;
// Do the appropriate thing
if (QueuedEvent->Type == Event) {
PerformWriteRequest(&QueuedEvent->Event.Request);
} else if (QueuedEvent->Type == Alert) {
// Don't even try to send failed alerts quicker than once a minute
NtQuerySystemTime(&Time);
RtlTimeToSecondsSince1970(&Time, &CurrentTime);
if (!LastAlertFailed || CurrentTime > LastAlertTried + 60) {
LastAlertFailed =
!SendAdminAlert(QueuedEvent->Event.Alert.MessageId,
QueuedEvent->Event.Alert.NumberOfStrings,
(PUNICODE_STRING)((PBYTE)QueuedEvent +
FIELD_OFFSET(ELF_QUEUED_EVENT, Event) +
sizeof(ELF_ALERT_RECORD)));
LastAlertTried = CurrentTime;
}
// Only try to write it for 5 minutes, then give up (the
// alerter service may not be configured to run)
if (LastAlertFailed
&&
QueuedEvent->Event.Alert.TimeOut > CurrentTime) {
bFlushEvent = FALSE;
}
}
// Move to the next one, saving this one to delete it
FlushEvent = QueuedEvent;
QueuedEvent = CONTAINING_RECORD(
QueuedEvent->Next.Flink,
struct _ELF_QUEUED_EVENT,
Next);
// Now remove this from the queue and free it if we successfully
// processed it
if (bFlushEvent) {
UnlinkQueuedEvent(FlushEvent);
ElfpFreeBuffer(FlushEvent);
}
}
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
RtlReleaseResource(&ElfModule->LogFile->Resource);
}
VOID
FlushQueuedEvents(
VOID
)
/*++
Routine Description:
This routine runs the list of queued events and frees them.
In order to protect against multiple thread/process access to the
list at the same time, we use an exclusive resource.
Arguments:
NONE
Return Value:
NONE
Note:
--*/
{
PELF_QUEUED_EVENT QueuedEvent;
PELF_QUEUED_EVENT FlushEvent;
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
// Walk the linked list and and free the memory for any events
QueuedEvent = CONTAINING_RECORD(
QueuedEventListHead.Flink,
struct _ELF_QUEUED_EVENT,
Next);
while (QueuedEvent->Next.Flink != QueuedEventListHead.Flink) {
// Free each event as we get to it.
FlushEvent = QueuedEvent;
QueuedEvent = CONTAINING_RECORD(
QueuedEvent->Next.Flink,
struct _ELF_QUEUED_EVENT,
Next
);
ElfpFreeBuffer(FlushEvent);
}
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
}
VOID
UnlinkLogModule(
PLOGMODULE LogModule
)
/*++
Routine Description:
This routine unlinks the LogModule specified from the linked list.
In order to protect against multiple thread/process access to the
list at the same time, we use a critical section.
Arguments:
LogModule points to a context handle structure.
Return Value:
NONE
Note:
--*/
{
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
// Remove this entry
RemoveEntryList(&LogModule->ModuleList);
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
}
VOID
LinkLogModule(
PLOGMODULE LogModule,
ANSI_STRING* pModuleNameA
)
/*++
Routine Description:
This routine links the LogModule specified into the linked list.
In order to protect against multiple thread/process access to the
list at the same time, we use a critical section.
Arguments:
LogModule points to a context handle structure.
ANSI LogModule name.
Return Value:
NONE
Note:
--*/
{
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
// Add the atom for this module.
LogModule->ModuleAtom = AddAtomA(pModuleNameA->Buffer);
// Place structure at the beginning of the list.
InsertHeadList(&LogModuleHead, &LogModule->ModuleList);
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&LogModuleCritSec);
}
VOID
UnlinkLogFile(
PLOGFILE pLogFile
)
/*++
Routine Description:
This routine unlinks the LogFile structure specified from the linked
list of log files;
In order to protect against multiple thread/process access to the
list at the same time, we use a critical section.
Arguments:
pLogFile points to a log file structure.
Return Value:
NONE
Note:
--*/
{
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&LogFileCritSec);
// Remove this entry
RemoveEntryList(&pLogFile->FileList);
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&LogFileCritSec);
}
VOID
LinkLogFile(
PLOGFILE pLogFile
)
/*++
Routine Description:
This routine links the LogFile specified into the linked list of
log files.
In order to protect against multiple thread/process access to the
list at the same time, we use a critical section.
Arguments:
pLogFile points to a context handle structure.
Return Value:
NONE
Note:
--*/
{
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&LogFileCritSec);
// Place structure at the beginning of the list.
InsertHeadList(&LogFilesHead, &pLogFile->FileList);
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&LogFileCritSec);
}
VOID
GetGlobalResource(
DWORD Type
)
/*++
Routine Description:
This routine takes the global resource either for shared access or
exclusive access depending on the value of Type. It waits forever for
the resource to become available.
Arguments:
Type is one of ELF_GLOBAL_SHARED or ELF_GLOBAL_EXCLUSIVE.
Return Value:
NONE
Note:
--*/
{
BOOL Acquired;
if (Type & ELF_GLOBAL_SHARED) {
Acquired = RtlAcquireResourceShared(
&GlobalElfResource,
TRUE); // Wait forever
} else { // Assume EXCLUSIVE
Acquired = RtlAcquireResourceExclusive(
&GlobalElfResource,
TRUE); // Wait forever
}
ASSERT(Acquired); // This must always be TRUE.
}
VOID
ReleaseGlobalResource(
VOID
)
/*++
Routine Description:
This routine releases the global resource.
Arguments:
NONE
Return Value:
NONE
Note:
--*/
{
RtlReleaseResource(&GlobalElfResource);
}
VOID
InvalidateContextHandlesForLogFile(
PLOGFILE pLogFile
)
/*++
Routine Description:
This routine walks through the context handles and marks the ones
that point to the LogFile passed in as "invalid for read".
Arguments:
Pointer to log file structure.
Return Value:
NONE.
Note:
--*/
{
IELF_HANDLE LogHandle;
PLOGMODULE pLogModule;
// Lock the context handle list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
// Walk the linked list and mark any matching context handles as
// invalid.
LogHandle = CONTAINING_RECORD(
LogHandleListHead.Flink,
struct _IELF_HANDLE,
Next);
while (LogHandle->Next.Flink != LogHandleListHead.Flink) {
pLogModule = FindModuleStrucFromAtom(LogHandle->Atom);
ASSERT(pLogModule);
if (pLogFile == pLogModule->LogFile) {
LogHandle->Flags |= ELF_LOG_HANDLE_INVALID_FOR_READ;
}
LogHandle = CONTAINING_RECORD(
LogHandle->Next.Flink,
struct _IELF_HANDLE,
Next);
}
// Unlock the context handle list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
}
VOID
FixContextHandlesForRecord(
DWORD RecordOffset,
DWORD NewRecordOffset
)
/*++
Routine Description:
This routine makes sure that the record starting at RecordOffset isn't
the current record for any open handle. If it is, the handle is adjusted
to point to the next record.
Arguments:
RecordOffset - The byte offset in the log of the record that is about
to be overwritten.
NewStartingRecord - The new location to point the handle to (this is the
new first record)
Return Value:
NONE.
Note:
--*/
{
IELF_HANDLE LogHandle;
// Lock the context handle list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
// Walk the linked list and fix any matching context handles
LogHandle = CONTAINING_RECORD(
LogHandleListHead.Flink,
struct _IELF_HANDLE,
Next);
while (LogHandle->Next.Flink != LogHandleListHead.Flink) {
if (LogHandle->SeekBytePos == RecordOffset) {
LogHandle->SeekBytePos = NewRecordOffset;
}
LogHandle = CONTAINING_RECORD(
LogHandle->Next.Flink,
struct _IELF_HANDLE,
Next);
}
// Unlock the context handle list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
}
PLOGFILE
FindLogFileFromName(
PUNICODE_STRING pFileName
)
/*++
Routine Description:
This routine looks at all the log files to find one that matches
the name passed in.
Arguments:
Pointer to name of file.
Return Value:
Matching LOGFILE structure if file in use.
Note:
--*/
{
PLOGFILE pLogFile;
// Lock the linked list
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)&LogFileCritSec);
pLogFile = CONTAINING_RECORD(
LogFilesHead.Flink,
LOGFILE,
FileList);
while (pLogFile->FileList.Flink != LogFilesHead.Flink) {
if (wcscmp(pLogFile->LogFileName->Buffer, pFileName->Buffer) == 0)
break;
pLogFile = CONTAINING_RECORD(
pLogFile->FileList.Flink,
LOGFILE,
FileList);
}
// Unlock the linked list
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&LogFileCritSec);
return (pLogFile->FileList.Flink == LogFilesHead.Flink ? NULL : pLogFile);
}
VOID
ElfpCreateElfEvent(
IN ULONG EventId,
IN USHORT EventType,
IN USHORT EventCategory,
IN USHORT NumStrings,
IN LPWSTR* Strings,
IN LPVOID Data,
IN ULONG DataSize,
IN USHORT Flags
)
/*++
Routine Description:
This creates an request packet to write an event on behalf of the event
log service itself. It then queues this packet to a linked list for
writing later.
Arguments:
The fields to use to create the event record
Return Value:
None
Note:
--*/
{
PELF_QUEUED_EVENT QueuedEvent;
PWRITE_PKT WritePkt;
PEVENTLOGRECORD EventLogRecord;
PBYTE BinaryData;
ULONG RecordLength;
ULONG StringOffset, DataOffset;
USHORT StringsSize = 0;
USHORT i;
ULONG PadSize;
ULONG ModuleNameLen; // Length in bytes
ULONG zero = 0; // For pad bytes
LARGE_INTEGER Time;
ULONG LogTimeWritten;
PWSTR ReplaceStrings;
#define ELF_MODULE_NAME L"EventLog"
// LogTimeWritten
// We need to generate a time when the log is written. This
// gets written in the log so that we can use it to test the
// retention period when wrapping the file.
NtQuerySystemTime(&Time);
RtlTimeToSecondsSince1970(
&Time,
&LogTimeWritten);
// Figure out how big a buffer to allocate
ModuleNameLen = (wcslen(ELF_MODULE_NAME) + 1) * sizeof(WCHAR);
StringOffset = sizeof(EVENTLOGRECORD)
+ ModuleNameLen
+ ComputerNameLength;
// Calculate the length of strings so that we can see how
// much space is needed for that.
for (i = 0; i < NumStrings; i++) {
StringsSize += wcslen(Strings[i]) + 1;
}
// DATA OFFSET:
DataOffset = StringOffset + StringsSize * sizeof(WCHAR);
// Determine how big a buffer is needed for the queued event record.
RecordLength = sizeof(ELF_QUEUED_EVENT)
+ sizeof(WRITE_PKT)
+ DataOffset
+ DataSize
+ sizeof(RecordLength); // Size excluding pad bytes
// Determine how many pad bytes are needed to align to a DWORD
// boundary.
PadSize = sizeof(ULONG) - (RecordLength % sizeof(ULONG));
RecordLength += PadSize; // True size needed
// Allocate the buffer for the Eventlog record
QueuedEvent = (PELF_QUEUED_EVENT)ElfpAllocateBuffer(RecordLength);
WritePkt = (PWRITE_PKT)(QueuedEvent + 1);
if (QueuedEvent != NULL) {
// Fill up the event record
RecordLength -= (sizeof(ELF_QUEUED_EVENT) + sizeof(WRITE_PKT));
EventLogRecord = (PEVENTLOGRECORD)(WritePkt + 1);
EventLogRecord->Length = RecordLength;
EventLogRecord->TimeGenerated = LogTimeWritten;
EventLogRecord->Reserved = ELF_LOG_FILE_SIGNATURE;
EventLogRecord->TimeWritten = LogTimeWritten;
EventLogRecord->EventID = EventId;
EventLogRecord->EventType = EventType;
EventLogRecord->EventCategory = EventCategory;
EventLogRecord->ReservedFlags = 0;
EventLogRecord->ClosingRecordNumber = 0;
EventLogRecord->NumStrings = NumStrings;
EventLogRecord->StringOffset = StringOffset;
EventLogRecord->DataLength = DataSize;
EventLogRecord->DataOffset = DataOffset;
EventLogRecord->UserSidLength = 0;
EventLogRecord->UserSidOffset = StringOffset;
// Fill in the variable-length fields
// STRINGS
ReplaceStrings = (PWSTR)((PBYTE)EventLogRecord
+ StringOffset);
for (i = 0; i < NumStrings; i++) {
wcscpy(ReplaceStrings, Strings[i]);
ReplaceStrings += wcslen(Strings[i]) + 1;
}
// MODULENAME
BinaryData = (PBYTE)EventLogRecord + sizeof(EVENTLOGRECORD);
RtlMoveMemory(BinaryData,
ELF_MODULE_NAME,
ModuleNameLen);
// COMPUTERNAME
BinaryData += ModuleNameLen; // Now point to computername
RtlMoveMemory(BinaryData,
LocalComputerName,
ComputerNameLength);
// BINARY DATA
BinaryData = (PBYTE)((PBYTE)EventLogRecord + DataOffset);
RtlMoveMemory(BinaryData, Data, DataSize);
// PAD - Fill with zeros
BinaryData += DataSize;
RtlMoveMemory(BinaryData, &zero, PadSize);
// LENGTH at end of record
BinaryData += PadSize; // Point after pad bytes
((PULONG)BinaryData)[0] = RecordLength;
// Build the QueuedEvent Packet
QueuedEvent->Type = Event;
QueuedEvent->Event.Request.Pkt.WritePkt = WritePkt;
QueuedEvent->Event.Request.Module = ElfModule;
QueuedEvent->Event.Request.Flags = Flags;
QueuedEvent->Event.Request.LogFile = ElfModule->LogFile;
QueuedEvent->Event.Request.Command = ELF_COMMAND_WRITE;
QueuedEvent->Event.Request.Pkt.WritePkt->Buffer = EventLogRecord;
QueuedEvent->Event.Request.Pkt.WritePkt->Datasize = RecordLength;
// Now Queue it on the linked list
LinkQueuedEvent(QueuedEvent);
}
}
VOID
ElfpCreateQueuedAlert(
DWORD MessageId,
DWORD NumberOfStrings,
LPWSTR Strings[]
)
{
DWORD i;
DWORD RecordLength;
PELF_QUEUED_EVENT QueuedEvent;
PUNICODE_STRING UnicodeStrings;
LPWSTR pString;
PBYTE ptr;
LARGE_INTEGER Time;
ULONG CurrentTime;
// Turn the input strings into UNICODE_STRINGS and figure out how
// big to make the buffer to allocate
RecordLength = sizeof(UNICODE_STRING) * NumberOfStrings;
UnicodeStrings = ElfpAllocateBuffer(RecordLength);
if (!UnicodeStrings) {
return;
}
RecordLength += FIELD_OFFSET(ELF_QUEUED_EVENT, Event) +
sizeof(ELF_ALERT_RECORD);
for (i = 0; i < NumberOfStrings; i++) {
RtlInitUnicodeString(&UnicodeStrings[i], Strings[i]);
RecordLength += UnicodeStrings[i].MaximumLength;
}
// Now allocate what will be the real queued event
QueuedEvent = ElfpAllocateBuffer(RecordLength);
if (!QueuedEvent) {
ElfpFreeBuffer(UnicodeStrings);
return;
}
QueuedEvent->Type = Alert;
QueuedEvent->Event.Alert.MessageId = MessageId;
QueuedEvent->Event.Alert.NumberOfStrings = NumberOfStrings;
// If we can't send the alert in 5 minutes, give up
NtQuerySystemTime(&Time);
RtlTimeToSecondsSince1970(&Time, &CurrentTime);
QueuedEvent->Event.Alert.TimeOut = CurrentTime + 300;
// Move the array of UNICODE_STRINGS into the queued event and
// point UnicodeStrings at it. Then fix up the Buffer pointers.
ptr = (PBYTE)QueuedEvent + FIELD_OFFSET(ELF_QUEUED_EVENT, Event) +
sizeof(ELF_ALERT_RECORD);
RtlCopyMemory(ptr,
UnicodeStrings,
sizeof(UNICODE_STRING) * NumberOfStrings);
ElfpFreeBuffer(UnicodeStrings);
UnicodeStrings = (PUNICODE_STRING)ptr;
pString = (LPWSTR)(ptr + sizeof(UNICODE_STRING) * NumberOfStrings);
for (i = 0; i < NumberOfStrings; i++) {
RtlMoveMemory(pString, UnicodeStrings[i].Buffer,
UnicodeStrings[i].MaximumLength);
UnicodeStrings[i].Buffer = pString;
pString =
(LPWSTR)((PBYTE)pString + UnicodeStrings[i].MaximumLength);
}
LinkQueuedEvent(QueuedEvent);
}
VOID
ElfpCreateQueuedMessage(
DWORD MessageId,
DWORD NumberOfStrings,
LPWSTR Strings[]
)
{
DWORD i;
DWORD RecordLength = 0;
PELF_QUEUED_EVENT QueuedEvent;
LPWSTR pString;
// Figure out how big to make the buffer to allocate
RecordLength = sizeof(ELF_QUEUED_EVENT);
for (i = 0; i < NumberOfStrings; i++) {
RecordLength += (wcslen(Strings[i]) + 1) * sizeof(WCHAR);
}
// Now allocate what will be the real queued event
QueuedEvent = ElfpAllocateBuffer(RecordLength);
if (!QueuedEvent) {
return;
}
QueuedEvent->Type = Message;
QueuedEvent->Event.Message.MessageId = MessageId;
QueuedEvent->Event.Message.NumberOfStrings = NumberOfStrings;
// Move the array of UNICODE strings into the queued event
pString = (LPWSTR)((PBYTE)QueuedEvent +
FIELD_OFFSET(ELF_QUEUED_EVENT, Event) +
sizeof(ELF_MESSAGE_RECORD));
for (i = 0; i < NumberOfStrings; i++) {
wcscpy(pString, Strings[i]);
pString += wcslen(Strings[i]) + 1;
}
LinkQueuedMessage(QueuedEvent);
}