summaryrefslogtreecommitdiffstats
path: root/private/eventlog
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/eventlog
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/eventlog')
-rw-r--r--private/eventlog/dirs31
-rw-r--r--private/eventlog/elf.acf7
-rw-r--r--private/eventlog/elf.idl317
-rw-r--r--private/eventlog/elfclnt/apistub.c1577
-rw-r--r--private/eventlog/elfclnt/elfclntp.h44
-rw-r--r--private/eventlog/elfclnt/getconfg.c110
-rw-r--r--private/eventlog/elfclnt/makefile6
-rw-r--r--private/eventlog/elfclnt/rpcbind.c205
-rw-r--r--private/eventlog/elfclnt/sources59
-rw-r--r--private/eventlog/elfcommn.h75
-rw-r--r--private/eventlog/eltest/eltest.c1535
-rw-r--r--private/eventlog/eltest/makefile6
-rw-r--r--private/eventlog/eltest/sources45
-rw-r--r--private/eventlog/event.h59
-rw-r--r--private/eventlog/imports.h2
-rw-r--r--private/eventlog/imports.idl60
-rw-r--r--private/eventlog/makefil048
-rw-r--r--private/eventlog/misc/dirs4
-rw-r--r--private/eventlog/misc/logalert.c204
-rw-r--r--private/eventlog/netevent/dummy.c4
-rw-r--r--private/eventlog/netevent/event.rc11
-rw-r--r--private/eventlog/netevent/makefile6
-rw-r--r--private/eventlog/netevent/makefile.inc1
-rw-r--r--private/eventlog/netevent/netevent.def7
-rw-r--r--private/eventlog/netevent/sources20
-rw-r--r--private/eventlog/ntsdexts/elfexts.c767
-rw-r--r--private/eventlog/ntsdexts/elfexts.def10
-rw-r--r--private/eventlog/ntsdexts/elfexts.rc9
-rw-r--r--private/eventlog/ntsdexts/makefile6
-rw-r--r--private/eventlog/ntsdexts/sources40
-rw-r--r--private/eventlog/readme.txt103
-rw-r--r--private/eventlog/server/alert.c147
-rw-r--r--private/eventlog/server/cairo.c471
-rw-r--r--private/eventlog/server/config.c1007
-rw-r--r--private/eventlog/server/control.c699
-rw-r--r--private/eventlog/server/copy.c196
-rw-r--r--private/eventlog/server/daytona/makefile6
-rw-r--r--private/eventlog/server/daytona/sources69
-rw-r--r--private/eventlog/server/dirs25
-rw-r--r--private/eventlog/server/elfapi.c2235
-rw-r--r--private/eventlog/server/elfcfg.h66
-rw-r--r--private/eventlog/server/elfdata.c218
-rw-r--r--private/eventlog/server/elfdef.h478
-rw-r--r--private/eventlog/server/elfextrn.h99
-rw-r--r--private/eventlog/server/elflpc.c774
-rw-r--r--private/eventlog/server/elflpc.h54
-rw-r--r--private/eventlog/server/elfproto.h384
-rw-r--r--private/eventlog/server/elfrpc.c216
-rw-r--r--private/eventlog/server/elfsec.c920
-rw-r--r--private/eventlog/server/elfutil.c1737
-rw-r--r--private/eventlog/server/eventlog.c1510
-rw-r--r--private/eventlog/server/eventlog.def6
-rw-r--r--private/eventlog/server/eventlog.rc11
-rw-r--r--private/eventlog/server/eventp.h48
-rw-r--r--private/eventlog/server/file.c1088
-rw-r--r--private/eventlog/server/logclear.c621
-rw-r--r--private/eventlog/server/memory.c149
-rw-r--r--private/eventlog/server/operate.c3117
-rw-r--r--private/eventlog/server/terminat.c267
-rw-r--r--private/eventlog/test/buildreg.dat3
-rw-r--r--private/eventlog/test/makefile6
-rw-r--r--private/eventlog/test/sources89
-rw-r--r--private/eventlog/test/test.c1400
-rw-r--r--private/eventlog/test/testwin.c770
-rw-r--r--private/eventlog/test/testwina.c693
65 files changed, 24957 insertions, 0 deletions
diff --git a/private/eventlog/dirs b/private/eventlog/dirs
new file mode 100644
index 000000000..ecb7e74d1
--- /dev/null
+++ b/private/eventlog/dirs
@@ -0,0 +1,31 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+Note:
+
+ The 'misc' directory is temporary. Will be removed after PPC.
+
+!ENDIF
+
+DIRS=server \
+ elfclnt \
+ netevent \
+ misc
+
+
+OPTIONAL_DIRS=test
diff --git a/private/eventlog/elf.acf b/private/eventlog/elf.acf
new file mode 100644
index 000000000..678a08bd6
--- /dev/null
+++ b/private/eventlog/elf.acf
@@ -0,0 +1,7 @@
+[ implicit_handle( handle_t eventlog_handle )]
+interface eventlog
+{
+#if defined(_PPC_)
+[optimize( "s" )] ElfrChangeNotify();
+#endif
+}
diff --git a/private/eventlog/elf.idl b/private/eventlog/elf.idl
new file mode 100644
index 000000000..8685828d3
--- /dev/null
+++ b/private/eventlog/elf.idl
@@ -0,0 +1,317 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ELF.IDL
+
+Abstract:
+
+ Contains the Elfr APIs which are used to remote the Elf APIs to
+ the local or remote server via RPC.
+ Also contains the RPC specific data structures for these API.
+
+Author:
+
+ Rajen Shah (rajens) 02-Apr-1991
+
+Revision History:
+
+ 02-Apr-1991 RajenS
+ created
+
+--*/
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(82273FDC-E32A-18C3-3F78-827929DC23EA),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+//
+// Interface Keyword
+//
+
+interface eventlog
+
+//
+// Interface Body
+//
+
+{
+
+import "imports.idl"; // import all the include files
+#include "event.h"
+
+//
+// ELF Generic Handle used to bind from client to server.
+//
+
+typedef [handle,unique] LPWSTR EVENTLOG_HANDLE_W;
+typedef [handle,unique] LPSTR EVENTLOG_HANDLE_A;
+
+//
+// ELF RPC Context Handle (Internal definition of ELF_HANDLE)
+//
+
+typedef [context_handle] struct _IELF_HANDLE {
+ LIST_ENTRY Next;
+ ULONG Signature; // BUGBUG - For debug only
+ ULONG Flags; // e.g. "invalid"
+ ULONG GrantedAccess;
+ ATOM Atom;
+ ULONG SeekRecordPos;
+ ULONG SeekBytePos;
+ ULONG MajorVersion; // To identify caller
+ ULONG MinorVersion;
+ ULONG NameLength;
+ [size_is(NameLength)] WCHAR Name[]; // Keep this as last field
+} *IELF_HANDLE;
+typedef IELF_HANDLE *PIELF_HANDLE;
+
+//
+// UNICODE Function Prototypes
+//
+//
+// Only OpenEL and CloseEL need to take PIELF_HANDLE
+// parameters since they are the only ones to modify the handle.
+// The others will take IELF_HANDLE parameters instead.
+//
+
+// NOTE:
+// The names of the functions are short so that when RPC
+// prepends the interface name to the names, they are still
+// unique.
+//
+
+NTSTATUS
+ElfrClearELFW (
+ [in] IELF_HANDLE LogHandle,
+ [in,unique] PRPC_UNICODE_STRING BackupFileName
+ );
+
+NTSTATUS
+ElfrBackupELFW (
+ [in] IELF_HANDLE LogHandle,
+ [in] PRPC_UNICODE_STRING BackupFileName
+ );
+
+NTSTATUS
+ElfrCloseEL (
+ [in,out] PIELF_HANDLE LogHandle
+ );
+
+NTSTATUS
+ElfrDeregisterEventSource (
+ [in,out] PIELF_HANDLE LogHandle
+ );
+
+NTSTATUS
+ElfrNumberOfRecords(
+ [in] IELF_HANDLE LogHandle,
+ [out] PULONG NumberOfRecords
+ );
+
+NTSTATUS
+ElfrOldestRecord(
+ [in] IELF_HANDLE LogHandle,
+ [out] PULONG OldestRecordNumber
+ );
+
+NTSTATUS
+ElfrChangeNotify(
+ [in] IELF_HANDLE LogHandle,
+ [in] RPC_CLIENT_ID ClientId,
+ [in] ULONG Event
+ );
+
+//
+// The ModuleName is the one passed in by the caller of the API. This is
+// the one that is written to the log file.
+//
+// RegModuleName is generated by the client stub, and is the name that is
+// used to determine the default log file name from the registry. For
+// NT Product1, this will be "Application". NOTE: It is necessary to do this
+// now so that we can support viewing logs from a Product 2 system in the
+// future if we change our logging mechanism.
+//
+
+NTSTATUS
+ElfrOpenELW (
+ [in] EVENTLOG_HANDLE_W UNCServerName,
+ [in] PRPC_UNICODE_STRING ModuleName, // Passed in API call
+ [in] PRPC_UNICODE_STRING RegModuleName, // Used for registry
+ [in] ULONG MajorVersion,
+ [in] ULONG MinorVersion,
+ [out] PIELF_HANDLE LogHandle
+ );
+
+NTSTATUS
+ElfrRegisterEventSourceW (
+ [in] EVENTLOG_HANDLE_W UNCServerName,
+ [in] PRPC_UNICODE_STRING ModuleName, // Passed in API call
+ [in] PRPC_UNICODE_STRING RegModuleName, // Used for registry info
+ [in] ULONG MajorVersion,
+ [in] ULONG MinorVersion,
+ [out] PIELF_HANDLE LogHandle
+ );
+
+NTSTATUS
+ElfrOpenBELW (
+ [in] EVENTLOG_HANDLE_W UNCServerName,
+ [in] PRPC_UNICODE_STRING BackupFileName, // Passed in API call
+ [in] ULONG MajorVersion,
+ [in] ULONG MinorVersion,
+ [out] PIELF_HANDLE LogHandle
+ );
+
+//
+// BUGBUG - Fix this interface once RPC supports the ability to return
+// zero bytes. Then, Buffer will have a length_is(*NumberOfBytesRead)
+// added to it. For now, always transfer NumberOfBytesToRead bytes!
+//
+// The LogHandle is an [in,out] since the server side stores information
+// in it that pertains to the current seek position.
+//
+
+NTSTATUS
+ElfrReadELW (
+ [in] IELF_HANDLE LogHandle,
+ [in] ULONG ReadFlags,
+ [in] ULONG RecordOffset,
+ [in] ULONG NumberOfBytesToRead,
+ [out,size_is(NumberOfBytesToRead)] PBYTE Buffer,
+ [out] PULONG NumberOfBytesRead,
+ [out] PULONG MinNumberOfBytesNeeded
+ );
+
+NTSTATUS
+ElfrReportEventW (
+ [in] IELF_HANDLE LogHandle,
+ [in] ULONG Time,
+ [in] USHORT EventType,
+ [in] USHORT EventCategory,
+ [in] ULONG EventID,
+ [in] USHORT NumStrings,
+ [in] ULONG DataSize,
+ [in] PRPC_UNICODE_STRING ComputerName,
+ [in, unique] PRPC_SID UserSID,
+ [in, size_is(NumStrings), unique] PRPC_UNICODE_STRING Strings[*],
+ [in, size_is(DataSize), unique] PBYTE Data,
+ [in] USHORT Flags,
+ [in,out,unique] PULONG RecordNumber,
+ [in,out,unique] PULONG TimeWritten
+ );
+
+//
+// ANSI Function Prototypes
+//
+//
+// Only OpenEL needs to take PIELF_HANDLE
+// parameters since they are the only ones to modify the handle.
+// The others will take IELF_HANDLE parameters instead.
+//
+// NOTE that there is only one api for CloseEL since there are no
+// strings involved.
+//
+
+NTSTATUS
+ElfrClearELFA (
+ [in] IELF_HANDLE LogHandle,
+ [in,unique] PRPC_STRING BackupFileName
+ );
+
+NTSTATUS
+ElfrBackupELFA (
+ [in] IELF_HANDLE LogHandle,
+ [in] PRPC_STRING BackupFileName
+ );
+
+//
+// The ModuleName is the one passed in by the caller of the API. This is
+// the one that is written to the log file.
+//
+// RegModuleName is generated by the client stub, and is the name that is
+// used to determine the default log file name from the registry. For
+// NT Product1, this will be "Application". NOTE: It is necessary to do this
+// now so that we can support viewing logs from a Product 2 system in the
+// future if we change our logging mechanism.
+//
+
+NTSTATUS
+ElfrOpenELA (
+ [in] EVENTLOG_HANDLE_A UNCServerName,
+ [in] PRPC_STRING ModuleName, // Passed in API call
+ [in] PRPC_STRING RegModuleName, // Used for registry info
+ [in] ULONG MajorVersion,
+ [in] ULONG MinorVersion,
+ [out] PIELF_HANDLE LogHandle
+ );
+
+NTSTATUS
+ElfrRegisterEventSourceA (
+ [in] EVENTLOG_HANDLE_A UNCServerName,
+ [in] PRPC_STRING ModuleName, // Passed in API call
+ [in] PRPC_STRING RegModuleName, // Used for registry info
+ [in] ULONG MajorVersion,
+ [in] ULONG MinorVersion,
+ [out] PIELF_HANDLE LogHandle
+ );
+
+NTSTATUS
+ElfrOpenBELA (
+ [in] EVENTLOG_HANDLE_A UNCServerName,
+ [in] PRPC_STRING FileName, // Passed in API call
+ [in] ULONG MajorVersion,
+ [in] ULONG MinorVersion,
+ [out] PIELF_HANDLE LogHandle
+ );
+
+//
+// BUGBUG - Fix this interface once RPC supports the ability to return
+// zero bytes. Then, Buffer will have a length_is(*NumberOfBytesRead)
+// added to it. For now, always transfer NumberOfBytesToRead bytes!
+//
+// The LogHandle is an [in,out] since the server side stores information
+// in it that pertains to the current seek position.
+//
+
+NTSTATUS
+ElfrReadELA (
+ [in] IELF_HANDLE LogHandle,
+ [in] ULONG ReadFlags,
+ [in] ULONG RecordOffset,
+ [in] ULONG NumberOfBytesToRead,
+ [out,size_is(NumberOfBytesToRead)] PBYTE Buffer,
+ [out] PULONG NumberOfBytesRead,
+ [out] PULONG MinNumberOfBytesNeeded
+ );
+
+NTSTATUS
+ElfrReportEventA (
+ [in] IELF_HANDLE LogHandle,
+ [in] ULONG Time,
+ [in] USHORT EventType,
+ [in] USHORT EventCategory,
+ [in] ULONG EventID,
+ [in] USHORT NumStrings,
+ [in] ULONG DataSize,
+ [in] PRPC_STRING ComputerName,
+ [in, unique] PRPC_SID UserSID,
+ [in, size_is(NumStrings), unique] PRPC_STRING Strings[*],
+ [in, size_is(DataSize), unique] PBYTE Data,
+ [in] USHORT Flags,
+ [in,out,unique] PULONG RecordNumber,
+ [in,out,unique] PULONG TimeWritten
+ );
+
+
+}
diff --git a/private/eventlog/elfclnt/apistub.c b/private/eventlog/elfclnt/apistub.c
new file mode 100644
index 000000000..da62f55bf
--- /dev/null
+++ b/private/eventlog/elfclnt/apistub.c
@@ -0,0 +1,1577 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ APISTUB.C
+
+Abstract:
+
+ This module contains the client ends of the Elf APIs.
+
+
+Author:
+
+ Rajen Shah (rajens) 29-Jul-1991
+
+
+Revision History:
+
+ 29-Jul-1991 RajenS
+ Created
+
+--*/
+
+#include <elfclntp.h>
+#include <lmerr.h>
+#include <stdlib.h>
+#include <string.h>
+
+//
+// Global data
+//
+PUNICODE_STRING pGlobalComputerNameU;
+PANSI_STRING pGlobalComputerNameA;
+
+
+
+VOID
+w_GetComputerName ( )
+
+/*++
+
+Routine Description:
+
+ This routine gets the name of the computer. It checks the global
+ variable to see if the computer name has already been determined.
+ If not, it updates that variable with the name.
+ It does this for the UNICODE and the ANSI versions.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+
+--*/
+{
+ PUNICODE_STRING pNameU;
+ PANSI_STRING pNameA;
+ LPSTR pName;
+ NTSTATUS Error;
+ NTSTATUS status;
+
+
+ pNameU = MIDL_user_allocate (sizeof (UNICODE_STRING));
+ pNameA = MIDL_user_allocate (sizeof (ANSI_STRING));
+
+ if ((pNameU != NULL) && (pNameA != NULL)) {
+
+ if ((Error = ElfpGetComputerName (&pName)) == NERR_Success) {
+
+ //
+ // ElfpComputerName has allocated a buffer to contain the
+ // ASCII name of the computer. We use that for the ANSI
+ // string structure.
+ //
+ RtlInitAnsiString ( pNameA, pName );
+
+ } else {
+ //
+ // We could not get the computer name for some reason. Set up
+ // the golbal pointer to point to the NULL string.
+ //
+ RtlInitAnsiString ( pNameA, "\0");
+ }
+
+ //
+ // Set up the UNICODE_STRING structure.
+ //
+ status = RtlAnsiStringToUnicodeString (
+ pNameU,
+ pNameA,
+ TRUE
+ );
+
+ //
+ // If there was no error, set the global variables.
+ // Otherwise, free the buffer allocated by ElfpGetComputerName
+ // and leave the global variables unchanged.
+ //
+ if (NT_SUCCESS(status)) {
+
+ pGlobalComputerNameU = pNameU; // Set global variable if no error
+ pGlobalComputerNameA = pNameA; // Set global ANSI variable
+
+ } else {
+
+ DbgPrint("[ELFCLNT] GetComputerName - Error 0x%lx\n", status);
+ LocalFree(pName);
+ MIDL_user_free (pNameU); // Free the buffers
+ MIDL_user_free (pNameA);
+ }
+
+ }
+}
+
+
+
+
+PUNICODE_STRING
+TmpGetComputerNameW ( )
+
+/*++
+
+Routine Description:
+
+ This routine gets the UNICODE name of the computer. It checks the global
+ variable to see if the computer name has already been determined.
+ If not, it calls the worker routine to do that.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ Returns a pointer to the computer name, or a NULL.
+
+
+--*/
+{
+ if (pGlobalComputerNameU == NULL) {
+ w_GetComputerName();
+ }
+ return (pGlobalComputerNameU);
+}
+
+
+
+PANSI_STRING
+TmpGetComputerNameA ( )
+
+/*++
+
+Routine Description:
+
+ This routine gets the ANSI name of the computer. It checks the global
+ variable to see if the computer name has already been determined.
+ If not, it calls the worker routine to do that.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ Returns a pointer to the computer name, or a NULL.
+
+
+--*/
+{
+
+ if (pGlobalComputerNameA == NULL) {
+ w_GetComputerName();
+ }
+ return (pGlobalComputerNameA);
+}
+
+//
+// These APIs only have one interface, since they don't take or return strings
+//
+
+NTSTATUS
+ElfNumberOfRecords(
+ IN HANDLE LogHandle,
+ OUT PULONG NumberOfRecords
+ )
+{
+ NTSTATUS status;
+
+ //
+ // Make sure the output pointer is valid
+ //
+
+ if (!NumberOfRecords) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call service entry point
+
+ status = ElfrNumberOfRecords (
+ (IELF_HANDLE) LogHandle,
+ NumberOfRecords
+ );
+
+ }
+ RpcExcept (1) {
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+
+}
+
+NTSTATUS
+ElfOldestRecord(
+ IN HANDLE LogHandle,
+ OUT PULONG OldestRecordNumber
+ )
+{
+ NTSTATUS status;
+
+ //
+ //
+ // Make sure the output pointer is valid
+ //
+
+ if (!OldestRecordNumber) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call service entry point
+
+ status = ElfrOldestRecord (
+ (IELF_HANDLE) LogHandle,
+ OldestRecordNumber
+ );
+
+ }
+ RpcExcept (1) {
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+
+}
+
+NTSTATUS
+ElfChangeNotify(
+ IN HANDLE LogHandle,
+ IN HANDLE Event
+ )
+{
+
+ NTSTATUS status;
+ RPC_CLIENT_ID RpcClientId;
+ CLIENT_ID ClientId;
+
+ //
+ // Map the handles to something that RPC can understand
+ //
+
+ ClientId = NtCurrentTeb()->ClientId;
+ RpcClientId.UniqueProcess = (ULONG) ClientId.UniqueProcess;
+ RpcClientId.UniqueThread = (ULONG) ClientId.UniqueThread;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ // Call service entry point
+
+ status = ElfrChangeNotify (
+ (IELF_HANDLE) LogHandle,
+ RpcClientId,
+ (DWORD) Event
+ );
+
+ }
+ RpcExcept (1) {
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+
+}
+
+//
+// UNICODE APIs
+//
+
+NTSTATUS
+ElfOpenEventLogW (
+ IN PUNICODE_STRING UNCServerName,
+ IN PUNICODE_STRING LogName,
+ OUT PHANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfOpenEventLog API.
+
+ It creates an RPC binding for the server specified, and stores that
+ and additional data away. It returns a handle to the caller that can
+ be used to later on access the handle-specific information.
+
+Arguments:
+
+ UNCServerName - Server with which to bind for subsequent operations.
+
+ LogName - Supplies the name of the module for the logfile
+ to associate with this handle.
+
+ LogHandle - Location where log handle is to be returned.
+
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a handle that can be used
+ for subsequent Elf API calls.
+
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ UNICODE_STRING RegModuleName;
+ EVENTLOG_HANDLE_W ServerNameString;
+
+ //
+ // Make sure input & output pointers are valid
+ //
+
+ if (!LogHandle || !LogName || LogName->Length == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) {
+ ServerNameString = UNCServerName->Buffer;
+ } else {
+ ServerNameString = NULL;
+ }
+
+ RtlInitUnicodeString( &RegModuleName, UNICODE_NULL);
+
+ // Call service via RPC. Pass in major and minor version numbers.
+
+ *LogHandle = NULL; // Must be NULL so RPC fills it in
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ status = ElfrOpenELW(
+ ServerNameString,
+ (PRPC_UNICODE_STRING) LogName,
+ (PRPC_UNICODE_STRING) &RegModuleName,
+ ELF_VERSION_MAJOR,
+ ELF_VERSION_MINOR,
+ (PIELF_HANDLE) LogHandle
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+
+ return (status);
+}
+
+
+NTSTATUS
+ElfRegisterEventSourceW (
+ IN PUNICODE_STRING UNCServerName,
+ IN PUNICODE_STRING ModuleName,
+ OUT PHANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfRegisterEventSource API.
+
+ It creates an RPC binding for the server specified, and stores that
+ and additional data away. It returns a handle to the caller that can
+ be used to later on access the handle-specific information.
+
+Arguments:
+
+ UNCServerName - Server with which to bind for subsequent operations.
+
+ ModuleName - Supplies the name of the module to associate with
+ this handle.
+
+ LogHandle - Location where log handle is to be returned.
+
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a handle that can be used
+ for subsequent Elf API calls.
+
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ UNICODE_STRING RegModuleName;
+ EVENTLOG_HANDLE_W ServerNameString;
+
+ //
+ // Make sure input & output pointers are valid
+ //
+
+ if (!LogHandle || !ModuleName || ModuleName->Length == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) {
+ ServerNameString = UNCServerName->Buffer;
+ } else {
+ ServerNameString = NULL;
+ }
+
+ RtlInitUnicodeString( &RegModuleName, UNICODE_NULL);
+
+ // Call service via RPC. Pass in major and minor version numbers.
+
+ *LogHandle = NULL; // Must be NULL so RPC fills it in
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ status = ElfrRegisterEventSourceW(
+ ServerNameString,
+ (PRPC_UNICODE_STRING)ModuleName,
+ (PRPC_UNICODE_STRING)&RegModuleName,
+ ELF_VERSION_MAJOR,
+ ELF_VERSION_MINOR,
+ (PIELF_HANDLE) LogHandle
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+
+ return (status);
+}
+
+
+NTSTATUS
+ElfOpenBackupEventLogW (
+ IN PUNICODE_STRING UNCServerName,
+ IN PUNICODE_STRING BackupFileName,
+ OUT PHANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfOpenBackupEventLog API.
+
+ It creates an RPC binding for the server specified, and stores that
+ and additional data away. It returns a handle to the caller that can
+ be used to later on access the handle-specific information.
+
+Arguments:
+
+ UNCServerName - Server with which to bind for subsequent operations.
+
+ BackupFileName - Supplies the filename of the module to associate with
+ this handle.
+
+ LogHandle - Location where log handle is to be returned.
+
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a handle that can be used
+ for subsequent Elf API calls.
+
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ EVENTLOG_HANDLE_W ServerNameString;
+
+ //
+ // Make sure input & output pointers are valid
+ //
+
+ if (!LogHandle || !BackupFileName || BackupFileName->Length == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) {
+ ServerNameString = UNCServerName->Buffer;
+ } else {
+ ServerNameString = NULL;
+ }
+
+ // Call service via RPC. Pass in major and minor version numbers.
+
+ *LogHandle = NULL; // Must be NULL so RPC fills it in
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ status = ElfrOpenBELW(
+ ServerNameString,
+ (PRPC_UNICODE_STRING)BackupFileName,
+ ELF_VERSION_MAJOR,
+ ELF_VERSION_MINOR,
+ (PIELF_HANDLE) LogHandle
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+}
+
+
+
+NTSTATUS
+ElfClearEventLogFileW (
+ IN HANDLE LogHandle,
+ IN PUNICODE_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfClearEventLogFile API.
+ The call is passed to the eventlog service on the appropriate server
+ identified by LogHandle.
+
+
+Arguments:
+
+ LogHandle - Handle returned from a previous "Open" call. This is
+ used to identify the module and the server.
+
+ BackupFileName - Name of the file to back up the current log file.
+ NULL implies not to back up the file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS status;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call service entry point
+
+ status = ElfrClearELFW (
+ (IELF_HANDLE) LogHandle,
+ (PRPC_UNICODE_STRING)BackupFileName
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+
+}
+
+
+NTSTATUS
+ElfBackupEventLogFileW (
+ IN HANDLE LogHandle,
+ IN PUNICODE_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfBackupEventLogFile API.
+ The call is passed to the eventlog service on the appropriate server
+ identified by LogHandle.
+
+
+Arguments:
+
+ LogHandle - Handle returned from a previous "Open" call. This is
+ used to identify the module and the server.
+
+ BackupFileName - Name of the file to back up the current log file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS status;
+
+ //
+ // Make sure input pointers are valid
+ //
+
+ if (!BackupFileName || BackupFileName->Length == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call service entry point
+
+ status = ElfrBackupELFW (
+ (IELF_HANDLE) LogHandle,
+ (PRPC_UNICODE_STRING)BackupFileName
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+
+}
+
+
+NTSTATUS
+ElfCloseEventLog (
+ IN HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfCloseEventLog API.
+ It closes the RPC binding, and frees any memory allocated for the
+ handle.
+
+
+Arguments:
+
+ LogHandle - Handle returned from a previous "Open" call.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS status;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call server
+
+ status = ElfrCloseEL (
+ (PIELF_HANDLE) &LogHandle
+ );
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+
+}
+
+
+NTSTATUS
+ElfDeregisterEventSource (
+ IN HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfDeregisterEventSource API.
+ It closes the RPC binding, and frees any memory allocated for the
+ handle.
+
+
+Arguments:
+
+ LogHandle - Handle returned from a previous "Open" call.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS status;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call server
+
+ status = ElfrDeregisterEventSource (
+ (PIELF_HANDLE) &LogHandle
+ );
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+
+}
+
+
+
+NTSTATUS
+ElfReadEventLogW (
+ IN HANDLE LogHandle,
+ IN ULONG ReadFlags,
+ IN ULONG RecordNumber,
+ OUT PVOID Buffer,
+ IN ULONG NumberOfBytesToRead,
+ OUT PULONG NumberOfBytesRead,
+ OUT PULONG MinNumberOfBytesNeeded
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfReadEventLog API.
+
+Arguments:
+
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS status;
+ ULONG FlagBits;
+
+ //
+ // Make sure the output pointers are valid
+ //
+
+ if (!Buffer || !NumberOfBytesRead || !MinNumberOfBytesNeeded) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Ensure that the ReadFlags we got are valid.
+ // Make sure that one of each type of bit is set.
+ //
+ FlagBits = ReadFlags & (EVENTLOG_SEQUENTIAL_READ | EVENTLOG_SEEK_READ);
+
+ if ((FlagBits > 2) || (FlagBits == 0)) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ FlagBits = ReadFlags & (EVENTLOG_FORWARDS_READ | EVENTLOG_BACKWARDS_READ);
+
+ if ((FlagBits > 8) || (FlagBits == 0)) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call service
+
+ status = ElfrReadELW (
+ (IELF_HANDLE) LogHandle,
+ ReadFlags,
+ RecordNumber,
+ NumberOfBytesToRead,
+ Buffer,
+ NumberOfBytesRead,
+ MinNumberOfBytesNeeded
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ // Return status and bytes read/required.
+
+ return (status);
+
+}
+
+
+
+NTSTATUS
+ElfReportEventW (
+ IN HANDLE LogHandle,
+ IN USHORT EventType,
+ IN USHORT EventCategory OPTIONAL,
+ IN ULONG EventID,
+ IN PSID UserSid,
+ IN USHORT NumStrings,
+ IN ULONG DataSize,
+ IN PUNICODE_STRING *Strings,
+ IN PVOID Data,
+ IN USHORT Flags,
+ IN OUT PULONG RecordNumber OPTIONAL,
+ IN OUT PULONG TimeWritten OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfReportEvent API.
+
+Arguments:
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+Note:
+
+ The last three parameters (Flags, RecordNumber and TimeWritten) are
+ designed to be used by Security Auditing for the implementation of
+ paired events (associating a file open event with the subsequent file
+ close). This will not be implemented in Product 1, but the API is
+ defined to allow easier support of this in a later release.
+
+
+--*/
+{
+ NTSTATUS status;
+ PUNICODE_STRING pComputerNameU;
+ LARGE_INTEGER Time;
+ ULONG EventTime;
+
+ //
+ // Generate the time of the event. This is done on the client side
+ // since that is where the event occurred.
+ //
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(&Time,
+ &EventTime
+ );
+
+ //
+ // Generate the ComputerName of the client.
+ // We have to do this in the client side since this call may be
+ // remoted to another server and we would not necessarily have
+ // the computer name there.
+ //
+ pComputerNameU = TmpGetComputerNameW();
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call service
+
+ status = ElfrReportEventW (
+ (IELF_HANDLE) LogHandle,
+ EventTime,
+ EventType,
+ EventCategory,
+ EventID,
+ NumStrings,
+ DataSize,
+ (PRPC_UNICODE_STRING)pComputerNameU,
+ UserSid,
+ (PRPC_UNICODE_STRING *)Strings,
+ Data,
+ Flags,
+ RecordNumber,
+ TimeWritten
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+
+}
+
+
+//
+// ANSI APIs
+//
+
+NTSTATUS
+ElfOpenEventLogA (
+ IN PANSI_STRING UNCServerName,
+ IN PANSI_STRING LogName,
+ OUT PHANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfOpenEventLog API.
+
+ It creates an RPC binding for the server specified, and stores that
+ and additional data away. It returns a handle to the caller that can
+ be used to later on access the handle-specific information.
+
+Arguments:
+
+ UNCServerName - Server with which to bind for subsequent operations.
+
+ LogName - Supplies the name of the module for the logfile to
+ associate with this handle.
+
+ LogHandle - Location where log handle is to be returned.
+
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a handle that can be used
+ for subsequent Elf API calls.
+
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ANSI_STRING RegModuleName;
+ EVENTLOG_HANDLE_A ServerNameString;
+
+ //
+ // Make sure input & output pointers are valid
+ //
+
+ if (!LogHandle || !LogName || LogName->Length == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) {
+ ServerNameString = UNCServerName->Buffer;
+ } else {
+ ServerNameString = NULL;
+ }
+
+ RtlInitAnsiString( &RegModuleName, ELF_APPLICATION_MODULE_NAME_ASCII );
+
+ if ( NT_SUCCESS (status) ) {
+
+ // Call service via RPC. Pass in major and minor version numbers.
+
+ *LogHandle = NULL; // Must be NULL so RPC fills it in
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ status = ElfrOpenELA (
+ ServerNameString,
+ (PRPC_STRING) LogName,
+ (PRPC_STRING) &RegModuleName,
+ ELF_VERSION_MAJOR,
+ ELF_VERSION_MINOR,
+ (PIELF_HANDLE) LogHandle
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+
+ }
+
+ return (status);
+}
+
+
+NTSTATUS
+ElfRegisterEventSourceA (
+ IN PANSI_STRING UNCServerName,
+ IN PANSI_STRING ModuleName,
+ OUT PHANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfOpenEventLog API.
+
+ It creates an RPC binding for the server specified, and stores that
+ and additional data away. It returns a handle to the caller that can
+ be used to later on access the handle-specific information.
+
+Arguments:
+
+ UNCServerName - Server with which to bind for subsequent operations.
+
+ ModuleName - Supplies the name of the module to associate with
+ this handle.
+
+ LogHandle - Location where log handle is to be returned.
+
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a handle that can be used
+ for subsequent Elf API calls.
+
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ANSI_STRING RegModuleName;
+ EVENTLOG_HANDLE_A ServerNameString;
+
+ //
+ // Make sure input & output pointers are valid
+ //
+
+ if (!LogHandle || !ModuleName || ModuleName->Length == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) {
+ ServerNameString = UNCServerName->Buffer;
+ } else {
+ ServerNameString = NULL;
+ }
+
+ RtlInitAnsiString( &RegModuleName, ELF_APPLICATION_MODULE_NAME_ASCII );
+
+ if ( NT_SUCCESS (status) ) {
+
+ // Call service via RPC. Pass in major and minor version numbers.
+
+ *LogHandle = NULL; // Must be NULL so RPC fills it in
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ status = ElfrRegisterEventSourceA (
+ ServerNameString,
+ (PRPC_STRING)ModuleName,
+ (PRPC_STRING)&RegModuleName,
+ ELF_VERSION_MAJOR,
+ ELF_VERSION_MINOR,
+ (PIELF_HANDLE) LogHandle
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+
+ }
+
+ return (status);
+}
+
+
+
+NTSTATUS
+ElfOpenBackupEventLogA (
+ IN PANSI_STRING UNCServerName,
+ IN PANSI_STRING FileName,
+ OUT PHANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfOpenBackupEventLog API.
+
+ It creates an RPC binding for the server specified, and stores that
+ and additional data away. It returns a handle to the caller that can
+ be used to later on access the handle-specific information.
+
+Arguments:
+
+ UNCServerName - Server with which to bind for subsequent operations.
+
+ FileName - Supplies the filename of the logfile to associate with
+ this handle.
+
+ LogHandle - Location where log handle is to be returned.
+
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a handle that can be used
+ for subsequent Elf API calls.
+
+
+--*/
+{
+ EVENTLOG_HANDLE_A ServerNameString;
+ NTSTATUS status;
+
+ //
+ // Make sure input & output pointers are valid
+ //
+
+ if (!LogHandle || !FileName || FileName->Length == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ if ((UNCServerName != NULL) && (UNCServerName->Length != 0)) {
+ ServerNameString = UNCServerName->Buffer;
+ } else {
+ ServerNameString = NULL;
+ }
+
+ // Call service via RPC. Pass in major and minor version numbers.
+
+ *LogHandle = NULL; // Must be NULL so RPC fills it in
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+
+ RpcTryExcept {
+
+ status = ElfrOpenBELA (
+ ServerNameString,
+ (PRPC_STRING)FileName,
+ ELF_VERSION_MAJOR,
+ ELF_VERSION_MINOR,
+ (PIELF_HANDLE) LogHandle
+ );
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+}
+
+
+
+NTSTATUS
+ElfClearEventLogFileA (
+ IN HANDLE LogHandle,
+ IN PANSI_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfClearEventLogFile API.
+ The call is passed to the eventlog service on the appropriate server
+ identified by LogHandle.
+
+
+Arguments:
+
+ LogHandle - Handle returned from a previous "Open" call. This is
+ used to identify the module and the server.
+
+ BackupFileName - Name of the file to back up the current log file.
+ NULL implies not to back up the file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS status;
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call service entry point
+
+ status = ElfrClearELFA (
+ (IELF_HANDLE) LogHandle,
+ (PRPC_STRING)BackupFileName
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+
+}
+
+
+NTSTATUS
+ElfBackupEventLogFileA (
+ IN HANDLE LogHandle,
+ IN PANSI_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfBackupEventLogFile API.
+ The call is passed to the eventlog service on the appropriate server
+ identified by LogHandle.
+
+
+Arguments:
+
+ LogHandle - Handle returned from a previous "Open" call. This is
+ used to identify the module and the server.
+
+ BackupFileName - Name of the file to back up the current log file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS status;
+
+ //
+ // Make sure input pointers are valid
+ //
+
+ if (!BackupFileName || BackupFileName->Length == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call service entry point
+
+ status = ElfrBackupELFA (
+ (IELF_HANDLE) LogHandle,
+ (PRPC_STRING)BackupFileName
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+
+}
+
+
+
+NTSTATUS
+ElfReadEventLogA (
+ IN HANDLE LogHandle,
+ IN ULONG ReadFlags,
+ IN ULONG RecordNumber,
+ OUT PVOID Buffer,
+ IN ULONG NumberOfBytesToRead,
+ OUT PULONG NumberOfBytesRead,
+ OUT PULONG MinNumberOfBytesNeeded
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfReadEventLog API.
+
+Arguments:
+
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS status;
+ ULONG FlagBits;
+
+ //
+ // Make sure the output pointers are valid
+ //
+
+ if (!Buffer || !NumberOfBytesRead || !MinNumberOfBytesNeeded) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Ensure that the ReadFlags we got are valid.
+ // Make sure that one of each type of bit is set.
+ //
+ FlagBits = ReadFlags & (EVENTLOG_SEQUENTIAL_READ | EVENTLOG_SEEK_READ);
+
+ if ( (FlagBits == (EVENTLOG_SEQUENTIAL_READ | EVENTLOG_SEEK_READ))
+ || (FlagBits == 0)) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ FlagBits = ReadFlags & (EVENTLOG_FORWARDS_READ | EVENTLOG_BACKWARDS_READ);
+
+ if ( (FlagBits == (EVENTLOG_FORWARDS_READ | EVENTLOG_BACKWARDS_READ))
+ || (FlagBits == 0)) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call service
+
+ status = ElfrReadELA (
+ (IELF_HANDLE) LogHandle,
+ ReadFlags,
+ RecordNumber,
+ NumberOfBytesToRead,
+ Buffer,
+ NumberOfBytesRead,
+ MinNumberOfBytesNeeded
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ // Return status and bytes read/required.
+
+ return (status);
+
+}
+
+
+
+NTSTATUS
+ElfReportEventA (
+ IN HANDLE LogHandle,
+ IN USHORT EventType,
+ IN USHORT EventCategory OPTIONAL,
+ IN ULONG EventID,
+ IN PSID UserSid,
+ IN USHORT NumStrings,
+ IN ULONG DataSize,
+ IN PANSI_STRING *Strings,
+ IN PVOID Data,
+ IN USHORT Flags,
+ IN OUT PULONG RecordNumber OPTIONAL,
+ IN OUT PULONG TimeWritten OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the client DLL entry point for the ElfReportEvent API.
+
+Arguments:
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+Note:
+
+ The last three parameters (Flags, RecordNumber and TimeWritten) are
+ designed to be used by Security Auditing for the implementation of
+ paired events (associating a file open event with the subsequent file
+ close). This will not be implemented in Product 1, but the API is
+ defined to allow easier support of this in a later release.
+
+
+--*/
+{
+ NTSTATUS status;
+ PANSI_STRING pComputerNameA;
+ LARGE_INTEGER Time;
+ ULONG EventTime;
+
+ //
+ // Generate the time of the event. This is done on the client side
+ // since that is where the event occurred.
+ //
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(&Time,
+ &EventTime
+ );
+
+ //
+ // Generate the ComputerName of the client.
+ // We have to do this in the client side since this call may be
+ // remoted to another server and we would not necessarily have
+ // the computer name there.
+ //
+ pComputerNameA = TmpGetComputerNameA();
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call service
+
+ status = ElfrReportEventA (
+ (IELF_HANDLE) LogHandle,
+ EventTime,
+ EventType,
+ EventCategory,
+ EventID,
+ NumStrings,
+ DataSize,
+ (PRPC_STRING)pComputerNameA,
+ UserSid,
+ (PRPC_STRING*)Strings,
+ Data,
+ Flags,
+ RecordNumber,
+ TimeWritten
+ );
+
+ }
+ RpcExcept (1) {
+
+ status = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return (status);
+
+}
diff --git a/private/eventlog/elfclnt/elfclntp.h b/private/eventlog/elfclnt/elfclntp.h
new file mode 100644
index 000000000..ec0de3a67
--- /dev/null
+++ b/private/eventlog/elfclnt/elfclntp.h
@@ -0,0 +1,44 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elfclntp.h
+
+Abstract:
+
+ Common include file for all the client-side modules for the
+ event logging facility.
+
+Author:
+
+ Rajen Shah (rajens) 29-Jul-1991
+
+
+Revision History:
+
+ 29-Jul-1991 RajenS
+ Created
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <rpc.h>
+#include <ntrpcp.h>
+#include <lmcons.h>
+#include <lmerr.h>
+//#include <netlib.h>
+
+#include <elf.h>
+#include <elfcommn.h>
+
+
+NTSTATUS
+ElfpGetComputerName (
+ IN LPSTR *ComputerNamePtr);
diff --git a/private/eventlog/elfclnt/getconfg.c b/private/eventlog/elfclnt/getconfg.c
new file mode 100644
index 000000000..a947af787
--- /dev/null
+++ b/private/eventlog/elfclnt/getconfg.c
@@ -0,0 +1,110 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ getconfg.c
+
+Abstract:
+
+ This is a Hacked up version of getconfg.c stolen from
+ c:\nt\private\net\netlib. We need to make an rtl routine out
+ of this - something that is more globally available.
+ -Danl 9-3-91
+
+
+ This module contains routines for manipulating configuration
+ information. The following functions available are:
+
+ NetpGetComputerName
+
+ Currently configuration information is kept in NT.CFG.
+ Later it will be kept by the configuration manager.
+
+Author:
+
+ Dan Lafferty (danl) 09-Apr-1991
+
+Environment:
+
+ User Mode -Win32 (also uses nt RTL routines)
+
+Revision History:
+
+ 09-Apr-1991 danl
+ created
+
+--*/
+
+//#include <stdlib.h> // atol
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <ntdef.h>
+#include <ntstatus.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+//#include <ntlsa.h> //
+#include <windef.h>
+#include <winbase.h> // LocalAlloc
+#include <lmcons.h>
+#include <string.h>
+#include <lmerr.h>
+
+
+
+NTSTATUS
+ElfpGetComputerName (
+ IN LPSTR *ComputerNamePtr)
+
+/*++
+
+Routine Description:
+
+ This routine obtains the computer name from a persistent database,
+ by calling the GetcomputerNameA Win32 Base API
+
+ This routine assumes the length of the computername is no greater
+ than MAX_COMPUTERNAME_LENGTH, space for which it allocates using
+ LocalAlloc. It is necessary for the user to free that space using
+ LocalFree when finished.
+
+Arguments:
+
+ ComputerNamePtr - This is a pointer to the location where the pointer
+ to the computer name is to be placed.
+
+Return Value:
+
+ NERR_Success - If the operation was successful.
+
+ It will return assorted Net or Win32 or NT error messages if not.
+
+--*/
+{
+ DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1;
+
+ //
+ // Allocate a buffer to hold the largest possible computer name.
+ //
+
+ *ComputerNamePtr = LocalAlloc(LMEM_ZEROINIT, nSize);
+
+ if (*ComputerNamePtr == NULL) {
+ return (GetLastError());
+ }
+
+ //
+ // Get the computer name string into the locally allocated buffer
+ // by calling the Win32 GetComputerNameA API.
+ //
+
+ if (!GetComputerNameA(*ComputerNamePtr, &nSize)) {
+ LocalFree(*ComputerNamePtr);
+ *ComputerNamePtr = NULL;
+ return (GetLastError());
+ }
+
+ return (NERR_Success);
+}
diff --git a/private/eventlog/elfclnt/makefile b/private/eventlog/elfclnt/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/eventlog/elfclnt/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/eventlog/elfclnt/rpcbind.c b/private/eventlog/elfclnt/rpcbind.c
new file mode 100644
index 000000000..2cf13c1eb
--- /dev/null
+++ b/private/eventlog/elfclnt/rpcbind.c
@@ -0,0 +1,205 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ rpcbind.c
+
+Abstract:
+
+ Contains the RPC bind and un-bind routines for the Eventlog
+ client-side APIs.
+
+Author:
+
+ Rajen Shah (rajens) 30-Jul-1991
+
+Revision History:
+
+ 30-Jul-1991 RajenS
+ created
+
+--*/
+
+//
+// INCLUDES
+//
+#include <elfclntp.h>
+#include <lmsvc.h>
+
+#define SERVICE_EVENTLOG L"EVENTLOG"
+
+
+/****************************************************************************/
+handle_t
+EVENTLOG_HANDLE_W_bind (
+ EVENTLOG_HANDLE_W ServerName)
+
+/*++
+
+Routine Description:
+ This routine calls a common bind routine that is shared by all services.
+ This routine is called from the ElfOpenEventLog API client stub when
+ it is necessary to bind to a server.
+ The binding is done to allow impersonation by the server since that is
+ necessary for the API calls.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the
+ binding is unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t bindingHandle;
+ RPC_STATUS status;
+
+ status = RpcpBindRpc (
+ ServerName,
+ SERVICE_EVENTLOG,
+ NULL,
+ &bindingHandle);
+
+ // DbgPrint("EVENTLOG_bind: handle=%d\n",bindingHandle);
+ return( bindingHandle);
+}
+
+
+
+/****************************************************************************/
+void
+EVENTLOG_HANDLE_W_unbind (
+ EVENTLOG_HANDLE_W ServerName,
+ handle_t BindingHandle)
+
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by
+ all services.
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ RPC_STATUS status;
+
+ // DbgPrint("EVENTLOG_HANDLE_unbind: handle=%d\n",BindingHandle);
+ status = RpcpUnbindRpc ( BindingHandle);
+ return;
+
+ UNREFERENCED_PARAMETER(ServerName);
+
+}
+
+
+handle_t
+EVENTLOG_HANDLE_A_bind (
+ EVENTLOG_HANDLE_A ServerName)
+
+/*++
+
+Routine Description:
+
+ This routine calls EVENTLOG_HANDLE_W_bind to do the work.
+
+Arguments:
+
+ ServerName - A pointer to a UNICODE string containing the name of
+ the server to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the
+ binding is unsuccessful, a NULL will be returned.
+
+--*/
+{
+ UNICODE_STRING ServerNameU;
+ ANSI_STRING ServerNameA;
+ handle_t bindingHandle;
+
+ //
+ // Convert the ANSI string to a UNICODE string before calling the
+ // UNICODE routine.
+ //
+ RtlInitAnsiString (&ServerNameA, (PSTR)ServerName);
+
+ RtlAnsiStringToUnicodeString (
+ &ServerNameU,
+ &ServerNameA,
+ TRUE
+ );
+
+ bindingHandle = EVENTLOG_HANDLE_W_bind(
+ (EVENTLOG_HANDLE_W)ServerNameU.Buffer
+ );
+
+ RtlFreeUnicodeString (&ServerNameU);
+
+ return( bindingHandle);
+}
+
+
+
+/****************************************************************************/
+void
+EVENTLOG_HANDLE_A_unbind (
+ EVENTLOG_HANDLE_A ServerName,
+ handle_t BindingHandle)
+
+/*++
+
+Routine Description:
+
+ This routine calls EVENTLOG_HANDLE_W_unbind.
+
+Arguments:
+
+ ServerName - This is the ANSI name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UNICODE_STRING ServerNameU;
+ ANSI_STRING ServerNameA;
+
+ //
+ // Convert the ANSI string to a UNICODE string before calling the
+ // UNICODE routine.
+ //
+ RtlInitAnsiString (&ServerNameA, (PSTR)ServerName);
+
+ RtlAnsiStringToUnicodeString (
+ &ServerNameU,
+ &ServerNameA,
+ TRUE
+ );
+
+ EVENTLOG_HANDLE_W_unbind( (EVENTLOG_HANDLE_W)ServerNameU.Buffer,
+ BindingHandle );
+
+ RtlFreeUnicodeString (&ServerNameU);
+
+ return;
+}
diff --git a/private/eventlog/elfclnt/sources b/private/eventlog/elfclnt/sources
new file mode 100644
index 000000000..457c34587
--- /dev/null
+++ b/private/eventlog/elfclnt/sources
@@ -0,0 +1,59 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Rajen Shah (rajens) 2-Jul-1991
+
+
+Revision History:
+
+!ENDIF
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+MAJORCOMP=eventlog
+MINORCOMP=client
+TARGETNAME=elfapi
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=LIBRARY
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+INCLUDES=.;..;$(BASEDIR)\private\inc
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+SOURCES= \
+ elf_c.c \
+ apistub.c \
+ rpcbind.c \
+ getconfg.c
+
+C_DEFINES=$(C_DEFINES) -DRPC_NO_WINDOWS_H -D_ADVAPI32_
diff --git a/private/eventlog/elfcommn.h b/private/eventlog/elfcommn.h
new file mode 100644
index 000000000..6c49d6bdb
--- /dev/null
+++ b/private/eventlog/elfcommn.h
@@ -0,0 +1,75 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elfcommn.h
+
+Abstract:
+
+ Common defines for client and server.
+
+Author:
+
+ Rajen Shah (rajens) 12-Aug-1991
+
+Revision History:
+
+--*/
+
+#ifndef _ELFCOMMON_
+#define _ELFCOMMON_
+
+//
+// Current default names of modules supported
+//
+
+#define ELF_MAX_LOG_MODULES 256
+
+#define ELF_SYSTEM_MODULE_NAME L"System"
+#define ELF_APPLICATION_MODULE_NAME L"Application"
+#define ELF_SECURITY_MODULE_NAME L"Security"
+
+#define ELF_SYSTEM_MODULE_NAME_ASCII "System"
+#define ELF_APPLICATION_MODULE_NAME_ASCII "Application"
+#define ELF_SECURITY_MODULE_NAME_ASCII "Security"
+
+
+//
+// Version numbers for the file header and the client
+//
+
+#define ELF_VERSION_MAJOR 0x0001
+#define ELF_VERSION_MINOR 0x0001
+
+//
+// The following are definitions for the Flags field in the context handle.
+//
+// ELF_LOG_HANDLE_INVALID is used to indicate that the handle is no
+// longer valid - i.e. the contents of the file
+// or the file itself have changed. It is used for
+// READs to cause the reader to "resync".
+//
+// ELF_LOG_HANDLE_BACKUP_LOG indicates that this was created with the
+// OpenBackupEventlog API and is not an active log.
+// This means we do some additional work at close time
+// and we disallow clear, backup, write and
+// ChangeNotify operations.
+//
+// ELF_LOG_HANDLE_REMOTE_HANDLE indicates that this handle was created via
+// a remote RPC call. This handle cannot be used for
+// ElfChangeNotify
+//
+// ELF_LOG_HANDLE_GENERATE_ON_CLOSE indicates that NtCloseAuditAlarm must
+// be called when this handle is closed. This flag
+// is set when an audit is generated on open.
+//
+
+#define ELF_LOG_HANDLE_INVALID_FOR_READ 0x0001
+#define ELF_LOG_HANDLE_BACKUP_LOG 0x0002
+#define ELF_LOG_HANDLE_REMOTE_HANDLE 0x0004
+#define ELF_LOG_HANDLE_LAST_READ_FORWARD 0x0008
+#define ELF_LOG_HANDLE_GENERATE_ON_CLOSE 0x0010
+
+#endif /* _ELFCOMMON_ */
diff --git a/private/eventlog/eltest/eltest.c b/private/eventlog/eltest/eltest.c
new file mode 100644
index 000000000..67d0812e2
--- /dev/null
+++ b/private/eventlog/eltest/eltest.c
@@ -0,0 +1,1535 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ELTEST.C
+
+Abstract:
+
+ Test Routines for the EventLog.
+
+THINGS I WANT THIS TO DO...
+ AddReg <ServerName> <logname> <EntryName> <EventMessageFile>
+ <CategoryMessageFile> <CategoryCount> <ParameterMessageFile>
+ <TypesSupported> - Creates A Registry Entry.
+
+ eltest addreg application mytest mf= eltest.dll cat=
+
+ CreateMessageFile <??? Is this possible ???>
+
+ WriteEvent <ServerName> <EventSource> <Type> <Category> <EventId> <UserSid?>
+ <NumStrings> <Strings> <RawData>
+
+ ReadLog <Server> <LogFile> <ReadFlags> <RecordOffset> <bufSize>
+ If LogFile isn't one of the popular ones, then it could be a backup
+ logfile.
+
+ GetNumEvents <Server> <LogFile>
+
+ GetOldest <Server> <LogFile>
+
+ Clear <Server> <LogFile>
+
+ Backup <Server> <LogFile> <BackupFile>
+
+
+ LOOPTESTS....
+ I should be able to run this test like mprtest such that it doesn't leave
+ the test process until told. This way we can register an event source,
+ then if we call WriteEvent without a specified EventSource, it will use
+ the stored source. Calling RegisterEventSource twice without calling
+ DeRegisterSource would be an error. (Or better yet, I could keep a table
+ of sources and handles).
+
+ RegisterEventSource <EventSource>
+ DeRegisterSource <EventSource>
+
+
+
+
+PROTOTYPES FOR FUNCTION....
+
+BOOL
+CloseEventLog (
+ HANDLE hEventLog
+ )
+BOOL
+DeregisterEventSource (
+ HANDLE hEventLog
+ )
+
+BOOL
+NotifyChangeEventLog(
+ HANDLE hEventLog,
+ HANDLE hEvent
+ )
+BOOL
+GetNumberOfEventLogRecords (
+ HANDLE hEventLog,
+ PDWORD NumberOfRecords
+ )
+BOOL
+GetOldestEventLogRecord (
+ HANDLE hEventLog,
+ PDWORD OldestRecord
+ )
+BOOL
+ClearEventLogW (
+ HANDLE hEventLog,
+ LPCWSTR BackupFileName
+ )
+BOOL
+BackupEventLogW (
+ HANDLE hEventLog,
+ LPCWSTR BackupFileName
+ )
+HANDLE
+OpenEventLogW (
+ LPCWSTR UNCServerName,
+ LPCWSTR ModuleName
+ )
+HANDLE
+RegisterEventSourceW (
+ LPCWSTR UNCServerName,
+ LPCWSTR ModuleName
+ )
+HANDLE
+OpenBackupEventLogW (
+ LPCWSTR UNCServerName,
+ LPCWSTR FileName
+ )
+BOOL
+ReadEventLogW (
+ HANDLE hEventLog,
+ DWORD dwReadFlags,
+ DWORD dwRecordOffset,
+ LPVOID lpBuffer,
+ DWORD nNumberOfBytesToRead,
+ DWORD *pnBytesRead,
+ DWORD *pnMinNumberOfBytesNeeded
+ )
+BOOL
+ReportEventW (
+ HANDLE hEventLog,
+ WORD wType,
+ WORD wCategory OPTIONAL,
+ DWORD dwEventID,
+ PSID lpUserSid OPTIONAL,
+ WORD wNumStrings,
+ DWORD dwDataSize,
+ LPCWSTR *lpStrings OPTIONAL,
+ LPVOID lpRawData OPTIONAL
+ )
+
+
+
+
+Author:
+
+ Dan Lafferty (danl) 09-March-1994
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 09-Mar-1994 danl
+ created
+
+--*/
+
+//
+// INCLUDES
+//
+#define UNICODE 1
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <nturtl.h> // needed for winbase.h
+
+
+#include <stdlib.h> // atoi
+#include <stdio.h> // printf
+#include <conio.h> // getch
+#include <string.h> // strcmp
+#include <windows.h> // win32 typedefs
+#include <tstr.h> // Unicode
+#include <debugfmt.h> // FORMAT_LPTSTR
+
+//------------------
+// DEFINES
+//------------------
+#define APPLICATION_LOG "Application"
+#define SYSTEM_LOG "System"
+#define SECURITY_LOG "Security"
+
+#define REG_APPLICATION_KEY "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"
+#define REG_SYSTEM_KEY "SYSTEM\\CurrentControlSet\\Services\\EventLog\\System\\"
+#define REG_SECURITY_KEY "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Security\\"
+
+#define EVENT_SOURCE_NAME "tevent"
+#define MSG_DLL "%SystemRoot%\\System32\\tevent.dll"
+
+#define VALUE_EVENT_MF TEXT("EventMessageFile")
+#define VALUE_CATEGORY_MF TEXT("CategoryMessageFile")
+#define VALUE_PARAMETER_MF TEXT("ParameterMessageFile")
+#define VALUE_TYPES_SUPPORTED TEXT("TypesSupported")
+#define VALUE_CATEGORY_COUNT TEXT("CategoryCount")
+
+#define TYPES_SUPPORTED (EVENTLOG_ERROR_TYPE | \
+ EVENTLOG_WARNING_TYPE | \
+ EVENTLOG_INFORMATION_TYPE)
+//----------------------
+// GLOBALS
+//----------------------
+ LPTSTR ApplLogRegName=TEXT(REG_APPLICATION_KEY);
+ LPTSTR SysLogRegName =TEXT(REG_SYSTEM_KEY);
+ LPTSTR SecLogRegName =TEXT(REG_SECURITY_KEY);
+ LPTSTR ApplLogName = TEXT(APPLICATION_LOG);
+ LPTSTR SysLogName = TEXT(SYSTEM_LOG);
+ LPTSTR SecLogName = TEXT(SECURITY_LOG);
+
+//----------------------
+// FUNCTION PROTOTYPES
+//----------------------
+
+VOID
+AddRegUsage(VOID);
+
+DWORD
+AddSourceToRegistry(
+ IN LPTSTR ServerName,
+ IN LPTSTR LogName,
+ IN LPTSTR EventSourceName,
+ IN LPTSTR *argv,
+ IN DWORD argc
+ );
+
+BOOL
+ConvertToUnicode(
+ OUT LPWSTR *UnicodeOut,
+ IN LPSTR AnsiIn
+ );
+
+DWORD
+DelSourceInRegistry(
+ IN LPTSTR ServerName,
+ IN LPTSTR LogName,
+ IN LPTSTR EventSourceName
+ );
+
+VOID
+DisplayStatus (
+ IN LPTSTR ServiceName,
+ IN LPTSTR DisplayName,
+ IN LPSERVICE_STATUS ServiceStatus
+ );
+
+BOOL
+MakeArgsUnicode (
+ DWORD argc,
+ PCHAR argv[]
+ );
+
+BOOL
+ProcessArgs (
+ LPTSTR ServerName,
+ DWORD argc,
+ LPTSTR argv[]
+ );
+
+VOID
+Usage(
+ VOID);
+
+VOID
+ConfigUsage(VOID);
+
+VOID
+CreateUsage(VOID);
+
+VOID
+QueryUsage(VOID);
+
+LONG
+wtol(
+ IN LPWSTR string
+ );
+
+VOID
+UserInputLoop(
+ LPTSTR ServerName
+ );
+DWORD
+ReadLogFile(
+ LPTSTR ServerName,
+ LPTSTR LogName,
+ IN LPTSTR *argv,
+ IN DWORD argc
+ );
+VOID
+ReadLogUsage(VOID);
+
+VOID
+DisplayRecord(
+ PEVENTLOGRECORD pElRecord,
+ BOOL PrintTheHeader
+ );
+
+/****************************************************************************/
+VOID _CRTAPI1
+main (
+ DWORD argc,
+ PCHAR argvAnsi[]
+ )
+
+/*++
+
+Routine Description:
+
+ Allows manual testing of the EVENTLOG API.
+
+ eltest
+
+
+
+Arguments:
+
+
+
+Return Value:
+
+
+
+--*/
+{
+ UCHAR i;
+ DWORD j;
+ DWORD argIndex;
+ LPTSTR pServerName=NULL;
+ LPTSTR *argv;
+
+ if (argc <2) {
+ Usage();
+ return;
+ }
+
+ //
+ // Make the arguments unicode if necessary.
+ //
+#ifdef UNICODE
+ if (!MakeArgsUnicode(argc, argvAnsi)) {
+ return;
+ }
+#endif
+
+ argv = (LPTSTR *)argvAnsi;
+
+ argIndex = 1;
+ if (STRNCMP (argv[1], TEXT("\\\\"), 2) == 0) {
+ pServerName = argv[1];
+ argIndex = 2; // skip over servername.
+ }
+
+ //
+ // Check to see if we are to run in Loop Mode, or in single function
+ // mode. In Loop Mode, we go into a loop, and ask the user for
+ // input until the user decides to quit.
+ //
+ // Process Arguments:
+ //
+ // INDEX 0 1 2 3
+ // EL <ServerName> <Function> <FunctionOptions...>
+ //
+
+ if (STRICMP (argv[argIndex], TEXT("Loop")) == 0) {
+ UserInputLoop(pServerName);
+ }
+ else {
+ ProcessArgs(pServerName, argc-argIndex, &(argv[argIndex]));
+ }
+
+
+#ifdef UNICODE
+ //
+ // Free up the unicode strings if there are any
+ //
+ for(j=0; j<argc; j++) {
+ LocalFree(argv[j]);
+ }
+#endif
+
+ return;
+}
+
+VOID
+UserInputLoop(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ This function sits in a loop, gathering input from the user, and
+ processing that input until the user indicates that it should stop.
+ The following user commands indicate that we should stop:
+ done
+ exit
+ stop
+ quit
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ UCHAR i;
+ DWORD j;
+ LPTSTR *argv;
+ UCHAR buffer[255];
+ LPSTR argvA[20];
+ DWORD argc=0;
+ BOOL KeepGoing;
+
+ do {
+ //------------------------------
+ // Get input from the user
+ //------------------------------
+ buffer[0] = 90-2;
+
+ printf("\nwaiting for instructions... \n");
+ cgets(buffer);
+
+ if (buffer[1] > 0) {
+ //--------------------------------------
+ // put the string in argv/argc format.
+ //--------------------------------------
+ buffer[1]+=2; // make this an end offset
+ argc=0;
+ for (i=2,j=0; i<buffer[1]; i++,j++) {
+ argc++;
+ argvA[j] = &(buffer[i]);
+ while ((buffer[i] != ' ') && (buffer[i] != '\0')) {
+ i++;
+ }
+ buffer[i] = '\0';
+ }
+
+ //------------------------------------------
+ // Make the arguments unicode if necessary.
+ //------------------------------------------
+#ifdef UNICODE
+
+ if (!MakeArgsUnicode(argc, argvA)) {
+ return;
+ }
+
+#endif
+ //-----------------------------------------------
+ // If the first argument doesn't indicate that
+ // we should stop, then process the arguments.
+ //-----------------------------------------------
+ argv = (LPTSTR *)argvA;
+
+ if((STRICMP (argv[0], TEXT("done")) == 0) ||
+ (STRICMP (argv[0], TEXT("stop")) == 0) ||
+ (STRICMP (argv[0], TEXT("exit")) == 0) ||
+ (STRICMP (argv[0], TEXT("quit")) == 0)) {
+ KeepGoing = FALSE;
+ }
+ else {
+ KeepGoing = ProcessArgs(ServerName, argc, argv);
+ }
+
+#ifdef UNICODE
+ //-----------------------------------------------
+ // Free up the unicode strings if there are any
+ //-----------------------------------------------
+ for(j=0; j<argc; j++) {
+ LocalFree(argv[j]);
+ }
+#endif
+ }
+ } while (KeepGoing);
+
+ return;
+
+}
+
+/****************************************************************************/
+BOOL
+ProcessArgs (
+ LPTSTR ServerName,
+ DWORD argc,
+ LPTSTR argv[]
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+
+Return Value:
+
+
+
+--*/
+
+{
+ DWORD status;
+ DWORD specialFlag = FALSE;
+ DWORD argIndex; // index to unchecked portion of arglist.
+
+
+ argIndex = 0;
+
+ //
+ // If we are adding a registry entry, the get a handle to it.
+ // Otherwise, get a handle to the LogFile.
+ //
+ //-----------------------
+ // AddSourceToRegistry
+ //-----------------------
+ if (STRICMP (argv[argIndex], TEXT("AddReg")) == 0 ) {
+
+ //
+ // Must have at least "AddReg logname EntryName"
+ //
+ if (argc < (argIndex + 3)) {
+ AddRegUsage();
+ goto CleanExit;
+ }
+
+ status = AddSourceToRegistry(
+ ServerName,
+ argv[argIndex+1], // LogName
+ argv[argIndex+2], // SourceName
+ &argv[argIndex+1],
+ argc-(argIndex+2)
+ );
+ }
+ //-----------------------
+ // DeleteFromRegistry
+ //-----------------------
+ else if (STRICMP (argv[argIndex], TEXT("DelReg")) == 0) {
+ //
+ // Must have at least "DelReg logname EntryName"
+ //
+ if (argc < (argIndex + 3)) {
+ goto CleanExit;
+ }
+
+ status = DelSourceInRegistry(
+ ServerName,
+ argv[argIndex+1], // LogName
+ argv[argIndex+2] // SourceName
+ );
+
+ }
+ //-----------------------
+ // WriteEvent
+ //-----------------------
+ else if (STRICMP (argv[argIndex], TEXT("WriteEvent")) == 0) {
+ printf("In WriteEvent\n");
+ if (ServerName != NULL) {
+ printf("ServerName = "FORMAT_LPTSTR"\n",ServerName);
+ }
+ }
+ //-----------------------
+ // ReadLog
+ //-----------------------
+ else if (STRICMP (argv[argIndex], TEXT("ReadLog")) == 0) {
+ printf("In ReadLog\n");
+ if (ServerName != NULL) {
+ printf("ServerName = "FORMAT_LPTSTR"\n",ServerName);
+ }
+ //
+ // Must have at least "ReadLog logname"
+ //
+ if (argc < (argIndex + 2)) {
+ ReadLogUsage();
+ goto CleanExit;
+ }
+
+ status = ReadLogFile(
+ ServerName, // ServerName
+ argv[argIndex+1], // LogName
+ &argv[argIndex+1], // argv
+ argc-(argIndex+1)); // argc
+ }
+ //-----------------------
+ // GetNumEvents
+ //-----------------------
+ else if (STRICMP (argv[argIndex], TEXT("GetNumEvents")) == 0) {
+ printf("in GetNumEvents\n");
+ if (ServerName != NULL) {
+ printf("ServerName = "FORMAT_LPTSTR"\n",ServerName);
+ }
+ }
+ //-----------------------
+ // GetOldest
+ //-----------------------
+ else if (STRICMP (argv[argIndex], TEXT("GetOldest")) == 0) {
+ printf("in GetOldest\n");
+ if (ServerName != NULL) {
+ printf("ServerName = "FORMAT_LPTSTR"\n",ServerName);
+ }
+ }
+ //-----------------------
+ // ClearLog
+ //-----------------------
+ else if (STRICMP (argv[argIndex], TEXT("ClearLog")) == 0) {
+ printf("in ClearLog\n");
+ if (ServerName != NULL) {
+ printf("ServerName = "FORMAT_LPTSTR"\n",ServerName);
+ }
+ }
+ //-----------------------
+ // Backup
+ //-----------------------
+ else if (STRICMP (argv[argIndex], TEXT("Backup")) == 0) {
+ printf("in Backup\n");
+ if (ServerName != NULL) {
+ printf("ServerName = "FORMAT_LPTSTR"\n",ServerName);
+ }
+ }
+ //-----------------------
+ // RegisterSource
+ //-----------------------
+ else if (STRICMP (argv[argIndex], TEXT("RegisterSource")) == 0) {
+ printf("in RegisterSource\n");
+ if (ServerName != NULL) {
+ printf("ServerName = "FORMAT_LPTSTR"\n",ServerName);
+ }
+ }
+ //-----------------------
+ // DeRegisterSource
+ //-----------------------
+ else if (STRICMP (argv[argIndex], TEXT("DeRegisterSource")) == 0) {
+ printf("in DeRegisterSource\n");
+ if (ServerName != NULL) {
+ printf("ServerName = "FORMAT_LPTSTR"\n",ServerName);
+ }
+ }
+ //****************
+ // Exit Program
+ //****************
+ else if (STRICMP (argv[0], TEXT("Exit")) == 0) {
+ //
+ // THIS SHOULD CLOSE HANDLES.
+ //
+ return(FALSE);
+ }
+ else {
+ printf("Bad argument\n");
+ Usage();
+ }
+
+CleanExit:
+
+
+ return(TRUE);
+}
+
+
+
+BOOL
+MakeArgsUnicode (
+ DWORD argc,
+ PCHAR argv[]
+ )
+
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+Note:
+
+
+--*/
+{
+ DWORD i;
+
+ //
+ // ScConvertToUnicode allocates storage for each string.
+ // We will rely on process termination to free the memory.
+ //
+ for(i=0; i<argc; i++) {
+
+ if(!ConvertToUnicode( (LPWSTR *)&(argv[i]), argv[i])) {
+ printf("Couldn't convert argv[%d] to unicode\n",i);
+ return(FALSE);
+ }
+
+
+ }
+ return(TRUE);
+}
+
+BOOL
+ConvertToUnicode(
+ OUT LPWSTR *UnicodeOut,
+ IN LPSTR AnsiIn
+ )
+
+/*++
+
+Routine Description:
+
+ This function translates an AnsiString into a Unicode string.
+ A new string buffer is created by this function. If the call to
+ this function is successful, the caller must take responsibility for
+ the unicode string buffer that was allocated by this function.
+ The allocated buffer should be free'd with a call to LocalFree.
+
+ NOTE: This function allocates memory for the Unicode String.
+
+ BUGBUG: This should be changed to return either
+ ERROR_NOT_ENOUGH_MEMORY or ERROR_INVALID_PARAMETER
+
+Arguments:
+
+ AnsiIn - This is a pointer to an ansi string that is to be converted.
+
+ UnicodeOut - This is a pointer to a location where the pointer to the
+ unicode string is to be placed.
+
+Return Value:
+
+ TRUE - The conversion was successful.
+
+ FALSE - The conversion was unsuccessful. In this case a buffer for
+ the unicode string was not allocated.
+
+--*/
+{
+
+ NTSTATUS ntStatus;
+ DWORD bufSize;
+ UNICODE_STRING unicodeString;
+ ANSI_STRING ansiString;
+
+ //
+ // Allocate a buffer for the unicode string.
+ //
+
+ bufSize = (strlen(AnsiIn)+1) * sizeof(WCHAR);
+
+ *UnicodeOut = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (UINT)bufSize);
+
+ if (*UnicodeOut == NULL) {
+ printf("ScConvertToUnicode:LocalAlloc Failure %ld\n",GetLastError());
+ return(FALSE);
+ }
+
+ //
+ // Initialize the string structures
+ //
+ RtlInitAnsiString( &ansiString, AnsiIn);
+
+ unicodeString.Buffer = *UnicodeOut;
+ unicodeString.MaximumLength = (USHORT)bufSize;
+ unicodeString.Length = 0;
+
+ //
+ // Call the conversion function.
+ //
+ ntStatus = RtlAnsiStringToUnicodeString (
+ &unicodeString, // Destination
+ &ansiString, // Source
+ (BOOLEAN)FALSE); // Allocate the destination
+
+ if (!NT_SUCCESS(ntStatus)) {
+
+ printf("ScConvertToUnicode:RtlAnsiStringToUnicodeString Failure %lx\n",
+ ntStatus);
+
+ return(FALSE);
+ }
+
+ //
+ // Fill in the pointer location with the unicode string buffer pointer.
+ //
+ *UnicodeOut = unicodeString.Buffer;
+
+ return(TRUE);
+
+}
+
+/****************************************************************************/
+VOID
+DisplayStatus (
+ IN LPTSTR ServiceName,
+ IN LPTSTR DisplayName,
+ IN LPSERVICE_STATUS ServiceStatus
+ )
+
+/*++
+
+Routine Description:
+
+ Displays the service name and the service status.
+
+ |
+ |SERVICE_NAME: messenger
+ |DISPLAY_NAME: messenger
+ | TYPE : WIN32
+ | STATE : ACTIVE,STOPPABLE, PAUSABLE, ACCEPTS_SHUTDOWN
+ | EXIT_CODE : 0xC002001
+ | CHECKPOINT : 0x00000001
+ | WAIT_HINT : 0x00003f21
+ |
+
+Arguments:
+
+ ServiceName - This is a pointer to a string containing the name of
+ the service.
+
+ DisplayName - This is a pointer to a string containing the display
+ name for the service.
+
+ ServiceStatus - This is a pointer to a SERVICE_STATUS structure from
+ which information is to be displayed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ printf("\nSERVICE_NAME: "FORMAT_LPTSTR"\n", ServiceName);
+ if (DisplayName != NULL) {
+ printf("DISPLAY_NAME: "FORMAT_LPTSTR"\n", DisplayName);
+ }
+
+ printf(" TYPE : %lx ", ServiceStatus->dwServiceType);
+
+ switch(ServiceStatus->dwServiceType){
+ case SERVICE_WIN32_OWN_PROCESS:
+ printf("WIN32_OWN_PROCESS \n");
+ break;
+ case SERVICE_WIN32_SHARE_PROCESS:
+ printf("WIN32_SHARE_PROCESS \n");
+ break;
+ case SERVICE_WIN32:
+ printf("WIN32 \n");
+ break;
+ case SERVICE_ADAPTER:
+ printf("ADAPTER \n");
+ break;
+ case SERVICE_KERNEL_DRIVER:
+ printf("KERNEL_DRIVER \n");
+ break;
+ case SERVICE_FILE_SYSTEM_DRIVER:
+ printf("FILE_SYSTEM_DRIVER \n");
+ break;
+ case SERVICE_DRIVER:
+ printf("DRIVER \n");
+ break;
+ default:
+ printf(" ERROR \n");
+ }
+
+ printf(" STATE : %lx ", ServiceStatus->dwCurrentState);
+
+ switch(ServiceStatus->dwCurrentState){
+ case SERVICE_STOPPED:
+ printf("STOPPED ");
+ break;
+ case SERVICE_START_PENDING:
+ printf("START_PENDING ");
+ break;
+ case SERVICE_STOP_PENDING:
+ printf("STOP_PENDING ");
+ break;
+ case SERVICE_RUNNING:
+ printf("RUNNING ");
+ break;
+ case SERVICE_CONTINUE_PENDING:
+ printf("CONTINUE_PENDING ");
+ break;
+ case SERVICE_PAUSE_PENDING:
+ printf("PAUSE_PENDING ");
+ break;
+ case SERVICE_PAUSED:
+ printf("PAUSED ");
+ break;
+ default:
+ printf(" ERROR ");
+ }
+
+ //
+ // Print Controls Accepted Information
+ //
+
+ if (ServiceStatus->dwControlsAccepted & SERVICE_ACCEPT_STOP) {
+ printf("\n (STOPPABLE,");
+ }
+ else {
+ printf("\n (NOT_STOPPABLE,");
+ }
+
+ if (ServiceStatus->dwControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE) {
+ printf("PAUSABLE,");
+ }
+ else {
+ printf("NOT_PAUSABLE,");
+ }
+
+ if (ServiceStatus->dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN) {
+ printf("ACCEPTS_SHUTDOWN)\n");
+ }
+ else {
+ printf("IGNORES_SHUTDOWN)\n");
+ }
+
+ //
+ // Print Exit Code
+ //
+ printf(" WIN32_EXIT_CODE : %d\t(0x%lx)\n",
+ ServiceStatus->dwWin32ExitCode,
+ ServiceStatus->dwWin32ExitCode);
+ printf(" SERVICE_EXIT_CODE : %d\t(0x%lx)\n",
+ ServiceStatus->dwServiceSpecificExitCode,
+ ServiceStatus->dwServiceSpecificExitCode );
+
+ //
+ // Print CheckPoint & WaitHint Information
+ //
+
+ printf(" CHECKPOINT : 0x%lx\n", ServiceStatus->dwCheckPoint);
+ printf(" WAIT_HINT : 0x%lx\n", ServiceStatus->dwWaitHint );
+
+ return;
+}
+
+VOID
+Usage(
+ VOID)
+{
+ printf("DESCRIPTION:\n");
+ printf("\tEL is a command line program used for testing the eventlog \n");
+ printf("USAGE:\n");
+ printf("\tEL <ServerName> [Function] <FunctionOptions...> \n\n");
+ printf("\tThe option <server> has the form \"\\\\ServerName\"\n");
+ printf("\tFurther help on Functions can be obtained by typing: \"el [Function]\"\n");
+ printf("\tFunctions:\n"
+ "\t AddReg-----------Creates a registry entry for an event source.\n"
+ "\t DelReg-----------Deletes a registry entry.\n"
+ "\t WriteEvent-------Writes an event.\n"
+ "\t ReadLog----------Reads from the logfile.\n"
+ "\t GetNumEvents-----Gets the number of events in the specified log.\n"
+ "\t GetOldest--------Gets the record number for the oldest record"
+ "\t in the log\n"
+ "\t ClearLog---------Clears the specified Log.\n"
+ "\t Backup-----------Copies the specified log to a new file.\n"
+ "\t RegisterSource---Registers a name for the event source.\n"
+ "\t The handle is stored internally.\n"
+ "\t DeRegisterSource-Closes handle opened with RegSource.\n"
+ "\t NotifyChange-----A thread is created which gets notified of EL changes.\n");
+
+ printf("\n");
+}
+
+VOID
+AddRegUsage(VOID)
+{
+
+ printf("\nAdds a subkey under one of the logfiles listed in the registry.\n");
+ printf("SYNTAX: \n eltest addreg <ServerName> logfile <SubKeyName> <option1> <option2>...\n");
+ printf("ADDREG OPTIONS:\n");
+ printf("NOTE: The option name includes the equal sign.\n");
+
+ printf(" MsgFile= Name of Event Message File\n"
+ " CatFile= Name of Category Message File\n"
+ " ParamFile= Name of Parameter Message File\n"
+ " CatCount= Category Count\n"
+ " Type= <error|warning|information|AuditSuccess|AuditFailure|All>\n");
+ printf("EXAMPLE:\n eltest addreg application myapp MsgFile= MyMsgs.dll"
+ " Type= error Type= warning\n");
+
+}
+
+VOID
+ConfigUsage(VOID)
+{
+ printf("Modifies a service entry in the registry and Service Database.\n");
+ printf("SYNTAX: \nsc config <service> <option1> <option2>...\n");
+ printf("CONFIG OPTIONS:\n");
+ printf("NOTE: The option name includes the equal sign.\n"
+ " type= <own|share|kernel|filesys|rec|adapt|error>\n"
+ " start= <boot|system|auto|demand|disabled|error>\n"
+ " error= <normal|severe|critical|error|ignore>\n"
+ " binPath= <BinaryPathName>\n"
+ " group= <LoadOrderGroup>\n"
+ " tag= <yes|no>\n"
+ " depend= <Dependencies(space seperated)>\n"
+ " obj= <AccountName|ObjectName>\n"
+ " DisplayName= <display name>\n"
+ " password= <password> \n");
+}
+VOID
+CreateUsage(VOID)
+{
+ printf("Creates a service entry in the registry and Service Database.\n");
+ printf("SYNTAX: \nsc create <service> <option1> <option2>...\n");
+ printf("CREATE OPTIONS:\n");
+ printf("NOTE: The option name includes the equal sign.\n"
+ " type= <own|share|kernel|filesys|rec|error>\n"
+ " start= <boot|system|auto|demand|disabled|error>\n"
+ " error= <normal|severe|critical|error|ignore>\n"
+ " binPath= <BinaryPathName>\n"
+ " group= <LoadOrderGroup>\n"
+ " tag= <yes|no>\n"
+ " depend= <Dependencies(space seperated)>\n"
+ " obj= <AccountName|ObjectName>\n"
+ " DisplayName= <display name>\n"
+ " password= <password> \n");
+}
+
+VOID
+ReadLogUsage(VOID)
+{
+
+ printf("\nReads a logfile and dumps the contents.\n");
+ printf("SYNTAX: \n eltest readlog <ServerName> logfile <option1> <option2>...\n");
+ printf("READLOG OPTIONS:\n");
+ printf("NOTE: The option name includes the equal sign.\n");
+
+ printf(" ReadFlag= <fwd|back|seek|seq> (default = fwd) \n"
+ " RecordNum= record number where read should start (default=0)\n"
+ " BufSize= size of the buffer (default = 10000)\n");
+ printf("EXAMPLE:\n eltest addreg application myapp MsgFile= MyMsgs.dll"
+ " Type= error Type= warning\n");
+}
+
+DWORD
+AddSourceToRegistry(
+ IN LPTSTR ServerName,
+ IN LPTSTR LogName,
+ IN LPTSTR EventSourceName,
+ IN LPTSTR *argv,
+ IN DWORD argc
+ )
+
+/*++
+
+Routine Description:
+
+ This function writes to the registry all the information to register
+ this application as an event source.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ TCHAR tempName[MAX_PATH];
+ HKEY hKey;
+ DWORD dwStatus=NO_ERROR;
+ HKEY hRegistry=HKEY_LOCAL_MACHINE;
+
+ LPTSTR EventMessageFile=NULL;
+ LPTSTR CategoryMessageFile=NULL;
+ LPTSTR ParameterMessageFile=NULL;
+ DWORD dwTypes=0;
+ DWORD dwCategoryCount=0;
+ DWORD i;
+
+ //
+ // Look at the LogName, and generate the appropriate registry key
+ // path for that log.
+ //
+ if (STRICMP(LogName, ApplLogName) == 0) {
+ STRCPY(tempName, ApplLogRegName);
+ }
+ else if (STRICMP(LogName, SysLogName) == 0) {
+ STRCPY(tempName, SysLogRegName);
+ }
+ else if (STRICMP(LogName, SecLogName) == 0) {
+ STRCPY(tempName, SecLogRegName);
+ }
+ else {
+ printf("AddSourceToRegistry: Invalid LogName\n");
+ return(ERROR_INVALID_PARAMETER);
+ }
+ STRCAT(tempName, EventSourceName);
+
+
+ //
+ // Get Variable Arguments
+ //
+ for (i=0; i<argc ;i++ ) {
+ if (STRICMP(argv[i], TEXT("EventMsgFile=")) == 0) {
+ EventMessageFile = argv[i+1];
+ i++;
+ }
+ if (STRICMP(argv[i], TEXT("CategoryMsgFile=")) == 0) {
+ CategoryMessageFile = argv[i+1];
+ i++;
+ }
+ if (STRICMP(argv[i], TEXT("ParameterMsgFile=")) == 0) {
+ ParameterMessageFile = argv[i+1];
+ i++;
+ }
+ if (STRICMP(argv[i], TEXT("Type=")) == 0) {
+ //--------------------------------------------------------
+ // We want to allow for several arguments of type= in the
+ // same line. These should cause the different arguments
+ // to be or'd together.
+ //--------------------------------------------------------
+ if (STRICMP(argv[i+1],TEXT("error")) == 0) {
+ dwTypes |= EVENTLOG_ERROR_TYPE;
+ }
+ if (STRICMP(argv[i+1],TEXT("warning")) == 0) {
+ dwTypes |= EVENTLOG_WARNING_TYPE;
+ }
+ if (STRICMP(argv[i+1],TEXT("information")) == 0) {
+ dwTypes |= EVENTLOG_INFORMATION_TYPE;
+ }
+ if (STRICMP(argv[i+1],TEXT("AuditSuccess")) == 0) {
+ dwTypes |= EVENTLOG_AUDIT_SUCCESS;
+ }
+ if (STRICMP(argv[i+1],TEXT("AuditFailure")) == 0) {
+ dwTypes |= EVENTLOG_AUDIT_FAILURE;
+ }
+ if (STRICMP(argv[i+1],TEXT("All")) == 0) {
+ dwTypes |= (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
+ EVENTLOG_INFORMATION_TYPE | EVENTLOG_AUDIT_SUCCESS |
+ EVENTLOG_AUDIT_FAILURE);
+ }
+ else {
+ printf("Invalid Type\n");
+ AddRegUsage();
+ return(ERROR_INVALID_PARAMETER);
+ }
+ i++;
+ }
+ if (STRICMP(argv[i], TEXT("CategoryCount=")) == 0) {
+ dwCategoryCount = ATOL(argv[i+1]);
+ i++;
+ }
+
+ }
+
+ //
+ // Connect to the registry on the correct machine.
+ //
+ printf("Connect to Registry\n");
+ dwStatus = RegConnectRegistry(ServerName, HKEY_LOCAL_MACHINE, &hRegistry);
+ if (dwStatus != NO_ERROR) {
+ printf("RegConnectRegistry Failed %d\n",GetLastError());
+ return(dwStatus);
+ }
+
+ //
+ // Create the new key for this source
+ //
+ printf("Create Key\n");
+ dwStatus = RegCreateKey(hRegistry, tempName, &hKey);
+ if (dwStatus != ERROR_SUCCESS) {
+ printf("Couldn't create Source Key in registry %d\n",dwStatus);
+ return(dwStatus);
+ }
+ if (EventMessageFile != NULL) {
+ printf("Set EventMessageFile\n");
+ dwStatus = RegSetValueEx(
+ hKey,
+ VALUE_EVENT_MF,
+ 0,
+ REG_EXPAND_SZ,
+ (LPBYTE)EventMessageFile,
+ STRLEN(EventMessageFile) + sizeof(TCHAR));
+
+ if (dwStatus != ERROR_SUCCESS) {
+ printf("RegSetValue (messageFile) failed %d\n",GetLastError());
+ goto CleanExit;
+ }
+ }
+ //
+ // Set the Category Message File
+ //
+ if (CategoryMessageFile != NULL) {
+ printf("Set Category Message File\n");
+ dwStatus = RegSetValueEx(
+ hKey,
+ VALUE_CATEGORY_MF,
+ 0,
+ REG_EXPAND_SZ,
+ (LPBYTE)CategoryMessageFile,
+ STRLEN(CategoryMessageFile) + sizeof(TCHAR));
+
+ if (dwStatus != ERROR_SUCCESS) {
+ printf("RegSetValue (category mf) failed %d\n",GetLastError());
+ goto CleanExit;
+ }
+ }
+
+ //
+ // Set the Parameter Message File
+ //
+ if (ParameterMessageFile != NULL) {
+ printf("Set Parameter Message File\n");
+ dwStatus = RegSetValueEx(
+ hKey,
+ VALUE_PARAMETER_MF,
+ 0,
+ REG_EXPAND_SZ,
+ (LPBYTE)ParameterMessageFile,
+ STRLEN(ParameterMessageFile) + sizeof(TCHAR));
+
+ if (dwStatus != ERROR_SUCCESS) {
+ printf("RegSetValue (Parameter mf) failed %d\n",GetLastError());
+ goto CleanExit;
+ }
+ }
+
+ //
+ // Set the Types Supported
+ //
+ if (dwTypes != 0) {
+ printf("Set Types Supported\n");
+ dwStatus = RegSetValueEx(
+ hKey,
+ VALUE_TYPES_SUPPORTED,
+ 0,
+ REG_DWORD,
+ (LPBYTE) &dwTypes,
+ sizeof(DWORD));
+
+ if (dwStatus != ERROR_SUCCESS) {
+ printf("RegSetValue (TypesSupported) failed %d\n",GetLastError());
+ goto CleanExit;
+ }
+
+ }
+
+ //
+ // Set the Category Count
+ //
+ if (dwCategoryCount != 0) {
+ printf("Set CategoryCount\n");
+ dwStatus = RegSetValueEx(
+ hKey,
+ VALUE_CATEGORY_COUNT,
+ 0,
+ REG_DWORD,
+ (LPBYTE) &dwCategoryCount,
+ sizeof(DWORD));
+
+ if (dwStatus != ERROR_SUCCESS) {
+ printf("RegSetValue (CategoryCount) failed %d\n",GetLastError());
+ goto CleanExit;
+ }
+ }
+ dwStatus = NO_ERROR;
+CleanExit:
+ RegCloseKey(hKey);
+ RegCloseKey(hRegistry);
+ return(dwStatus);
+}
+
+DWORD
+DelSourceInRegistry(
+ IN LPTSTR ServerName,
+ IN LPTSTR LogName,
+ IN LPTSTR EventSourceName
+ )
+
+/*++
+
+Routine Description:
+
+ This function writes to the registry all the information to register
+ this application as an event source.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ LPTSTR tempName;
+ HKEY hParentKey;
+ BOOL status=FALSE;
+ DWORD dwStatus;
+ HKEY hRegistry=HKEY_LOCAL_MACHINE;
+
+
+ //
+ // Look at the LogName, and generate the appropriate registry key
+ // path for that log.
+ //
+ if (STRICMP(LogName, ApplLogName) == 0) {
+ tempName = ApplLogRegName;
+ }
+ else if (STRICMP(LogName, SysLogName) == 0) {
+ tempName = SysLogRegName;
+ }
+ else if (STRICMP(LogName, SecLogName) == 0) {
+ tempName = SecLogRegName;
+ }
+ else {
+ printf("AddSourceToRegistry: Invalid LogName\n");
+ return(ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Connect to the registry on the correct machine.
+ //
+ dwStatus = RegConnectRegistry(ServerName, HKEY_LOCAL_MACHINE, &hRegistry);
+ if (dwStatus != NO_ERROR) {
+ printf("RegConnectRegistry Failed %d\n",GetLastError());
+ return(status);
+ }
+
+ //
+ // Open the Parent Key of the key we want to delete.
+ //
+ dwStatus = RegOpenKeyEx(
+ hRegistry,
+ tempName,
+ 0,
+ KEY_ALL_ACCESS,
+ &hParentKey);
+
+ if (dwStatus != ERROR_SUCCESS) {
+ printf("Couldn't open Parent of key to be deleted. %d\n",dwStatus);
+ goto CleanExit;
+ }
+ //
+ // Delete the subkey.
+ //
+ dwStatus = RegDeleteKey(hParentKey, EventSourceName);
+ if (dwStatus != ERROR_SUCCESS) {
+ printf("Couldn't delete "FORMAT_LPTSTR" key from registry %d\n",
+ EventSourceName, dwStatus);
+ }
+
+ RegCloseKey(hParentKey);
+CleanExit:
+ RegCloseKey(hRegistry);
+ return(status);
+}
+
+DWORD
+ReadLogFile(
+ LPTSTR ServerName,
+ LPTSTR LogName,
+ IN LPTSTR *argv,
+ IN DWORD argc
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ DWORD dwReadFlag = EVENTLOG_FORWARDS_READ;
+ DWORD dwRecordNum = 0;
+ DWORD BufSize = 10000;
+ DWORD numBytesRead;
+ DWORD numBytesReqd;
+ LPVOID pElBuffer = NULL;
+ PEVENTLOGRECORD pElRecord;
+ BOOL PrintTheHeader;
+ DWORD i;
+ HANDLE hEventLog=NULL;
+
+ //
+ // Get Variable Arguments
+ //
+ for (i=0; i<argc ;i++ ) {
+ if (STRICMP(argv[i], TEXT("ReadFlag=")) == 0) {
+ if (STRICMP(argv[i+1],TEXT("fwd")) == 0) {
+ dwReadFlag |= EVENTLOG_FORWARDS_READ;
+ }
+ if (STRICMP(argv[i+1],TEXT("back")) == 0) {
+ dwReadFlag |= EVENTLOG_BACKWARDS_READ;
+ }
+ if (STRICMP(argv[i+1],TEXT("seek")) == 0) {
+ dwReadFlag |= EVENTLOG_SEEK_READ;
+ }
+ if (STRICMP(argv[i+1],TEXT("seq")) == 0) {
+ dwReadFlag |= EVENTLOG_SEQUENTIAL_READ;
+ }
+ i++;
+ }
+ if (STRICMP(argv[i], TEXT("RecordNum=")) == 0) {
+ dwRecordNum = ATOL(argv[i+1]);
+ i++;
+ }
+ if (STRICMP(argv[i], TEXT("BufSize=")) == 0) {
+ BufSize = ATOL(argv[i+1]);
+ i++;
+ }
+ hEventLog = OpenEventLog(ServerName,LogName);
+ if (hEventLog == NULL) {
+ printf("OpenEventLog failed %d\n",GetLastError());
+ return(0);
+ }
+ pElBuffer = LocalAlloc(LPTR, BufSize);
+ if (pElBuffer == NULL) {
+ printf("ReadLogFile: LocalAlloc Failed %d\n",GetLastError());
+ goto CleanExit;
+ }
+
+ //---------------------------------------------------------
+ // Read and Display the contents of the eventlog
+ //---------------------------------------------------------
+ PrintTheHeader = TRUE;
+TryAgain:
+ while(ReadEventLog(
+ hEventLog,
+ dwReadFlag,
+ dwRecordNum,
+ pElBuffer,
+ BufSize,
+ &numBytesRead,
+ &numBytesReqd)) {
+
+ pElRecord = (PEVENTLOGRECORD) pElBuffer;
+ while ((PBYTE) pElRecord < (PBYTE) pElBuffer + numBytesRead) {
+ //
+ // Print the record to the display
+ //
+ DisplayRecord(pElRecord,PrintTheHeader);
+ PrintTheHeader = FALSE;
+ //
+ // Move to the next event in the buffer
+ //
+ pElRecord = (PEVENTLOGRECORD)((PBYTE) pElRecord +
+ pElRecord->Length);
+ }
+ }
+ switch(GetLastError()) {
+ case ERROR_INSUFFICIENT_BUFFER:
+ //
+ // Increase the size of the buffer and try again
+ //
+ if (numBytesReqd > BufSize) {
+ LocalFree(pElBuffer);
+ BufSize = numBytesReqd;
+ pElBuffer = LocalAlloc(LPTR, BufSize);
+ if (!pElBuffer) {
+ printf("ReadLogFile: LocalAlloc Failed %d\n",GetLastError());
+ }
+ goto TryAgain;
+ }
+ else {
+ printf("ReadLogFile #1: THIS SHOULD NEVER HAPPEN\n");
+ }
+ break;
+ case ERROR_EVENTLOG_FILE_CHANGED:
+ //
+ // The current read position for this handle has been overwritten.
+ // Reopen the file and print a message to the effect that some
+ // records may have been missed.
+ //
+ printf("ReadLogFile: Current Read position has been overwritten\n");
+
+ hEventLog = OpenEventLog(ServerName,LogName);
+ if (hEventLog == NULL) {
+ printf("OpenEventLog failed %d\n",GetLastError());
+ goto CleanExit;
+ }
+ goto TryAgain;
+ case ERROR_HANDLE_EOF:
+ printf("EOF\n");
+ break;
+ default:
+ printf("UnknownError: %d\n",GetLastError());
+ break;
+ }
+ }
+CleanExit:
+ if (pElBuffer != NULL) {
+ LocalFree(pElBuffer);
+ }
+ if (hEventLog != NULL) {
+ CloseEventLog(hEventLog);
+ }
+ return(0);
+}
+
+VOID
+DisplayRecord(
+ PEVENTLOGRECORD pElRecord,
+ BOOL PrintTheHeader
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ if (PrintTheHeader) {
+ printf("RecNum/tTimeGen/tWriteTime/tEventID/tType/tNumStr/tCat/n");
+ }
+ printf("%d/t%d/t%d/t%d/t%d/t%d/t%d\n",
+ pElRecord->RecordNumber,
+ pElRecord->TimeGenerated,
+ pElRecord->TimeWritten,
+ pElRecord->EventID,
+ pElRecord->EventType,
+ pElRecord->NumStrings,
+ pElRecord->EventCategory);
+}
+
+LONG
+wtol(
+ IN LPWSTR string
+ )
+{
+ LONG value = 0;
+
+ while((*string != L'\0') &&
+ (*string >= L'0') &&
+ ( *string <= L'9')) {
+ value = value * 10 + (*string - L'0');
+ string++;
+ }
+
+ return(value);
+}
+
diff --git a/private/eventlog/eltest/makefile b/private/eventlog/eltest/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/eventlog/eltest/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/eventlog/eltest/sources b/private/eventlog/eltest/sources
new file mode 100644
index 000000000..00246b41a
--- /dev/null
+++ b/private/eventlog/eltest/sources
@@ -0,0 +1,45 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ sources for ELTEST.EXE
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Dan Lafferty (danl) 5-May-1991
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = private
+MINORCOMP = eventlog
+TARGETNAME= eltest
+
+
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+
+INCLUDES=.;..\..\inc
+
+
+SOURCES= eltest.c
+
+UMTYPE=console
+
diff --git a/private/eventlog/event.h b/private/eventlog/event.h
new file mode 100644
index 000000000..b735df5ca
--- /dev/null
+++ b/private/eventlog/event.h
@@ -0,0 +1,59 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ EVENT.H
+
+Abstract:
+
+ Contains the common data structures that should be put in one
+ place in the tree.
+
+Author:
+
+ Rajen Shah (rajens) 21-Aug-1991
+
+Revision History:
+
+--*/
+
+//
+// Switch to using the commonly defined (in ntdef.h) UNICODE_STRING
+// Do a munge to fix this if it works ok
+//
+
+typedef UNICODE_STRING RPC_UNICODE_STRING, *PRPC_UNICODE_STRING;
+
+
+//
+// RPC definition of the SID structure. Note the use of the [size_is()]
+// qualifier to specify the number of elements in the variable size
+// imbedded SubAuthorityCount array at runtime.
+//
+// BUGBUG - This is stolen from samrpc.idl, and should really be placed
+// in a common place for all to use.
+//
+typedef struct _RPC_SID {
+ UCHAR Revision;
+ UCHAR SubAuthorityCount;
+ SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
+ [size_is(SubAuthorityCount)] ULONG SubAuthority[*];
+} RPC_SID, *PRPC_SID, **PPRPC_SID;
+
+//
+// ANSI counted string
+//
+
+typedef struct _RPC_STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+// [size_is(MaximumLength+0), length_is(Length+1)] PCHAR Buffer;
+ [size_is(MaximumLength)] PCHAR Buffer;
+} RPC_STRING, *PRPC_STRING, RPC_ANSI_STRING, *PRPC_ANSI_STRING;
+
+typedef struct _RPC_CLIENT_ID {
+ ULONG UniqueProcess;
+ ULONG UniqueThread;
+} RPC_CLIENT_ID, *PRPC_CLIENT_ID;
diff --git a/private/eventlog/imports.h b/private/eventlog/imports.h
new file mode 100644
index 000000000..e09e76be2
--- /dev/null
+++ b/private/eventlog/imports.h
@@ -0,0 +1,2 @@
+#include <nt.h>
+#include <windef.h>
diff --git a/private/eventlog/imports.idl b/private/eventlog/imports.idl
new file mode 100644
index 000000000..b7ed62591
--- /dev/null
+++ b/private/eventlog/imports.idl
@@ -0,0 +1,60 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of types defined in other header files. The .idl file for the RPC
+ product should contain a line in the interface body that imports this
+ file. For example:
+
+ import "imports.idl";
+
+ Doing this causes the MIDL generated header file to contain the
+ #include lines that are in this file.
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <windef.h>, then the contents of
+ windef.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in windef.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+ 03-Apr-1991 danl
+ created
+
+--*/
+
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(0.0)
+]
+interface eventlog_imports
+
+{
+#define MIDL_PASS
+#define UNICODE
+
+#include "imports.h"
+
+}
diff --git a/private/eventlog/makefil0 b/private/eventlog/makefil0
new file mode 100644
index 000000000..d9488c71e
--- /dev/null
+++ b/private/eventlog/makefil0
@@ -0,0 +1,48 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+ELFIDL=elf
+
+CLIENT_FILES=elfclnt\$(ELFIDL)_c.c $(ELFIDL).h
+SERVER_FILES=server\$(ELFIDL)_s.c
+LOCAL_FILES=$(ELFIDL)_c.c $(ELFIDL)_s.c
+
+INCS= -I$(BASEDIR)\public\sdk\inc -I$(BASEDIR)\public\sdk\inc\crt
+
+ELFMIDLARGS=-oldnames -error allocation -error ref -ms_ext -c_ext \
+ -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(INCS)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_FILES) $(SERVER_FILES) del_elf_sources
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delete_source all
+
+delete_source:
+ -erase $(CLIENT_FILES) $(SERVER_FILES) $(LOCAL_FILES)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_FILES): $(ELFIDL).idl
+ midl -Oi $(ELFMIDLARGS) $(ELFIDL).idl
+ copy $(ELFIDL)_c.c elfclnt\$(ELFIDL)_c.c
+
+$(SERVER_FILES): $(ELFIDL).idl
+ midl -client none $(ELFMIDLARGS) $(ELFIDL).idl
+ copy $(ELFIDL)_s.c server\$(ELFIDL)_s.c
+
+del_elf_sources:
+ -del $(LOCAL_FILES) 1> NUL 2>NUL
diff --git a/private/eventlog/misc/dirs b/private/eventlog/misc/dirs
new file mode 100644
index 000000000..076ccc941
--- /dev/null
+++ b/private/eventlog/misc/dirs
@@ -0,0 +1,4 @@
+
+DIRS=
+
+OPTIONAL_DIRS=
diff --git a/private/eventlog/misc/logalert.c b/private/eventlog/misc/logalert.c
new file mode 100644
index 000000000..2b5e5f760
--- /dev/null
+++ b/private/eventlog/misc/logalert.c
@@ -0,0 +1,204 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ LOGALERT.C
+
+Abstract:
+
+ This file contains the routine to log a Cairo alert in the system log.
+
+
+Author:
+
+ Ravi Rudrappa (ravir) 18-Jan-1995
+
+
+Revision History:
+
+ 18-Jan-1995 RaviR
+ Created
+
+--*/
+
+#ifdef _CAIRO_
+
+#include <elfclntp.h>
+
+
+NTSTATUS
+ElfLogCairoAlertInSystemLog(
+ IN HANDLE hLogHandle,
+ IN LARGE_INTEGER liEventTime,
+ IN USHORT usEventType,
+ IN USHORT usEventCategory,
+ IN ULONG ulEventID,
+ IN USHORT usNumStrings,
+ IN ULONG ulDataSize,
+ IN WCHAR *pwszComputerName,
+ IN WCHAR **ppwszStrings,
+ IN PBYTE pbData
+ )
+/*++
+
+Routine Description:
+
+ This routine logs the given Cairo Alert in the given log, through the
+ ElfrReportEventW function.
+
+Arguments:
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+Note:
+
+
+--*/
+{
+ NTSTATUS s = STATUS_SUCCESS;
+ ULONG ulEventTime;
+ PRPC_SID psidUser = NULL;
+ USHORT usFlags;
+ PUNICODE_STRING pusComputerName = NULL;
+ PUNICODE_STRING *ppusStrings = NULL;
+ ULONG i;
+ ULONG ulNumofAllocatedStrings = 0;
+ LPBYTE pbString = NULL;
+
+ //
+ // parameter validation
+ //
+
+ if ((hLogHandle == NULL) ||
+ (pwszComputerName == NULL) ||
+ ((usNumStrings > 0) && (ppwszStrings == NULL)) ||
+ ((ulDataSize > 0) && (pbData == NULL)))
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // user Sid
+ //
+
+ psidUser = NULL; // BUGBUG: is this ok?
+
+ //
+ // Map creation time
+ //
+
+ RtlTimeToSecondsSince1970(&liEventTime, &ulEventTime);
+
+ //
+ // Convert the array of Alert description insert strings
+ // to an array of UNICODE_STRINGs.
+ //
+
+ if (usNumStrings > 0)
+ {
+ LPBYTE pbPtr;
+ //
+ // allocate memory in one shot.
+ //
+
+ pbPtr = pbString = MIDL_user_allocate(
+ sizeof(PUNICODE_STRING) * usNumStrings +
+ sizeof(UNICODE_STRING) * usNumStrings);
+
+ if (pbString == NULL)
+ {
+ s = STATUS_NO_MEMORY;
+ goto LCleanUp;
+ }
+
+ ppusStrings = (PUNICODE_STRING *)pbPtr;
+
+ pbPtr += sizeof(PUNICODE_STRING) * usNumStrings;
+
+ //
+ // For each string passed in, allocate a UNICODE_STRING structure
+ // and set it to the matching string.
+ //
+ for (i = 0; i < usNumStrings; i++)
+ {
+ ppusStrings[i] = (PUNICODE_STRING)pbPtr;
+
+ pbPtr += sizeof(UNICODE_STRING);
+
+ RtlInitUnicodeString(ppusStrings[i], ppwszStrings[i]);
+ }
+ }
+
+ //
+ // Map the ComputerName to UNICODE_STRING.
+ //
+
+ pusComputerName = MIDL_user_allocate(sizeof(UNICODE_STRING));
+
+ if (pusComputerName == NULL)
+ {
+ s = STATUS_NO_MEMORY;
+ goto LCleanUp;
+ }
+
+ RtlInitUnicodeString(pusComputerName, pwszComputerName);
+
+
+ //
+ // Do the RPC call with an exception handler since RPC will raise an
+ // exception if anything fails. It is up to us to figure out what
+ // to do once the exception is raised.
+ //
+ RpcTryExcept {
+
+ // Call service
+
+ s = ElfrReportEventW (
+ (IELF_HANDLE)hLogHandle,
+ ulEventTime,
+ usEventType,
+ usEventCategory,
+ ulEventID,
+ usNumStrings,
+ ulDataSize,
+ (PRPC_UNICODE_STRING)pusComputerName,
+ psidUser,
+ (PRPC_UNICODE_STRING *)ppusStrings,
+ pbData,
+ 0, // Flags,
+ NULL, // RecordNumber,
+ NULL); // TimeWritten
+
+ }
+ RpcExcept (1) {
+
+ s = I_RpcMapWin32Status(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+LCleanUp:
+
+ //
+ // Free the space allocated for the inserts
+ // and then free the space for ComputerName.
+ //
+ if (pbString)
+ {
+ MIDL_user_free(pbString);
+ }
+
+ if (pusComputerName)
+ {
+ MIDL_user_free(pusComputerName);
+ }
+
+ return (s);
+
+}
+
+#endif // _CAIRO_
diff --git a/private/eventlog/netevent/dummy.c b/private/eventlog/netevent/dummy.c
new file mode 100644
index 000000000..537f11ef3
--- /dev/null
+++ b/private/eventlog/netevent/dummy.c
@@ -0,0 +1,4 @@
+void
+DummyEntryPoint(void)
+{
+}
diff --git a/private/eventlog/netevent/event.rc b/private/eventlog/netevent/event.rc
new file mode 100644
index 000000000..658dbbd75
--- /dev/null
+++ b/private/eventlog/netevent/event.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Net Event Handler"
+#define VER_INTERNALNAME_STR "NetEvent.Dll"
+#define VER_ORIGINALFILENAME_STR "NetEvent.Dll"
+
+#include "common.ver"
+#include "netevent.rc"
diff --git a/private/eventlog/netevent/makefile b/private/eventlog/netevent/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/eventlog/netevent/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/eventlog/netevent/makefile.inc b/private/eventlog/netevent/makefile.inc
new file mode 100644
index 000000000..387f6df16
--- /dev/null
+++ b/private/eventlog/netevent/makefile.inc
@@ -0,0 +1 @@
+event.rc: netevent.rc MSG00001.bin
diff --git a/private/eventlog/netevent/netevent.def b/private/eventlog/netevent/netevent.def
new file mode 100644
index 000000000..5f3a626e8
--- /dev/null
+++ b/private/eventlog/netevent/netevent.def
@@ -0,0 +1,7 @@
+LIBRARY NETEVENT
+
+DESCRIPTION 'Network event messages'
+
+EXPORTS
+
+ DummyEntryPoint
diff --git a/private/eventlog/netevent/sources b/private/eventlog/netevent/sources
new file mode 100644
index 000000000..76dbce398
--- /dev/null
+++ b/private/eventlog/netevent/sources
@@ -0,0 +1,20 @@
+MAJORCOMP=evenlog
+MINORCOMP=netevent
+
+TARGETNAME=netevent
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+INCLUDES=.
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=dummy.c \
+ event.rc
+
+UMRES=obj\*\event.res
+
+NTTARGETFILE0=event.rc
diff --git a/private/eventlog/ntsdexts/elfexts.c b/private/eventlog/ntsdexts/elfexts.c
new file mode 100644
index 000000000..69fb5f25c
--- /dev/null
+++ b/private/eventlog/ntsdexts/elfexts.c
@@ -0,0 +1,767 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ elfexts.c
+
+Abstract:
+
+ This function contains the eventlog ntsd debugger extensions
+
+Author:
+
+ Dan Hinsley (DanHi) 22-May-1993
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <ntsdexts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <time.h>
+#include <elf.h>
+#include <elfdef.h>
+#include <elfcommn.h>
+#include <elfproto.h>
+#include <svcs.h>
+#include <elfextrn.h>
+
+//#define DbgPrint(_x_) (lpOutputRoutine) _x_
+#define DbgPrint(_x_)
+#define MAX_NAME 256
+#define printf (lpOutputRoutine)
+#define GET_DATA(DebugeeAddr, LocalAddr, Length) \
+ Status = ReadProcessMemory( \
+ GlobalhCurrentProcess, \
+ (LPVOID)DebugeeAddr, \
+ LocalAddr, \
+ Length, \
+ NULL \
+ );
+
+PNTSD_OUTPUT_ROUTINE lpOutputRoutine;
+PNTSD_GET_EXPRESSION lpGetExpressionRoutine;
+PNTSD_CHECK_CONTROL_C lpCheckControlCRoutine;
+
+HANDLE GlobalhCurrentProcess;
+BOOL Status;
+
+//
+// Initialize the global function pointers
+//
+
+VOID
+InitFunctionPointers(
+ HANDLE hCurrentProcess,
+ PNTSD_EXTENSION_APIS lpExtensionApis
+ )
+{
+ //
+ // Load these to speed access if we haven't already
+ //
+
+ if (!lpOutputRoutine) {
+ lpOutputRoutine = lpExtensionApis->lpOutputRoutine;
+ lpGetExpressionRoutine = lpExtensionApis->lpGetExpressionRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+ }
+
+ //
+ // Stick this in a global
+ //
+
+ GlobalhCurrentProcess = hCurrentProcess;
+}
+
+LPWSTR
+GetUnicodeString(
+ PUNICODE_STRING pUnicodeString
+ )
+{
+ DWORD Pointer;
+ UNICODE_STRING UnicodeString;
+
+ GET_DATA(pUnicodeString, &UnicodeString, sizeof(UNICODE_STRING))
+ Pointer = (DWORD) UnicodeString.Buffer;
+ UnicodeString.Buffer = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
+ UnicodeString.Length + sizeof(WCHAR));
+ GET_DATA(Pointer, UnicodeString.Buffer, UnicodeString.Length)
+
+ return(UnicodeString.Buffer);
+}
+
+DWORD
+GetLogFileAddress(
+ LPSTR LogFileName,
+ PLOGFILE LogFile
+ )
+{
+ ANSI_STRING AnsiString;
+ UNICODE_STRING UnicodeString;
+ DWORD Pointer;
+ DWORD LogFileAnchor;
+ LPWSTR ModuleName;
+
+ //
+ // Convert the string to UNICODE
+ //
+
+ RtlInitAnsiString(&AnsiString, LogFileName);
+ RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE);
+
+ //
+ // Walk the logfile list looking for a match
+ //
+
+ LogFileAnchor = (lpGetExpressionRoutine)("LogFilesHead");
+
+ GET_DATA(LogFileAnchor, &Pointer, sizeof(DWORD))
+
+ while (Pointer != LogFileAnchor) {
+ GET_DATA(Pointer, LogFile, sizeof(LOGFILE))
+ ModuleName = GetUnicodeString(LogFile->LogModuleName);
+ if (!wcsicmp(ModuleName, UnicodeString.Buffer)) {
+ break;
+ }
+ LocalFree(ModuleName);
+ Pointer = (DWORD) LogFile->FileList.Flink;
+ }
+
+ RtlFreeUnicodeString(&UnicodeString);
+
+ if (Pointer == LogFileAnchor) {
+ return(0);
+ }
+ else {
+ LocalFree(ModuleName);
+ return(Pointer);
+ }
+}
+
+//
+// Dump an individual record
+//
+
+DWORD
+DumpRecord(
+ DWORD Record,
+ DWORD RecordNumber,
+ DWORD StartOfFile,
+ DWORD EndOfFile
+ )
+{
+ DWORD BufferLen;
+ PCHAR TimeBuffer;
+ PEVENTLOGRECORD EventLogRecord;
+ LPWSTR Module;
+ LPWSTR Computer;
+ DWORD FirstPiece = 0;
+
+ GET_DATA(Record, &BufferLen, sizeof(DWORD))
+
+ //
+ // See if it's a ELF_SKIP_DWORD, and if it is, return the top of the
+ // file
+ //
+
+ if (BufferLen == ELF_SKIP_DWORD) {
+ return(StartOfFile + sizeof(ELF_LOGFILE_HEADER));
+ }
+
+ //
+ // See if it's the EOF record
+ //
+
+ if (BufferLen == ELFEOFRECORDSIZE) {
+ return(0);
+ }
+
+ BufferLen += sizeof(DWORD); // get room for length of next record
+ EventLogRecord = (PEVENTLOGRECORD) LocalAlloc(LMEM_ZEROINIT, BufferLen);
+
+ //
+ // If the record wraps, grab it piecemeal
+ //
+
+ if (EndOfFile && BufferLen + Record > EndOfFile) {
+ FirstPiece = EndOfFile - Record;
+ GET_DATA(Record, EventLogRecord, FirstPiece);
+ GET_DATA((StartOfFile + sizeof(ELF_LOGFILE_HEADER)),
+ ((PBYTE) EventLogRecord + FirstPiece), BufferLen - FirstPiece)
+ }
+ else {
+ GET_DATA(Record, EventLogRecord, BufferLen)
+ }
+
+ //
+ // If it's greater than the starting record, print it out
+ //
+
+ if (EventLogRecord->RecordNumber >= RecordNumber) {
+ printf("\nRecord %d is %d [0x%X] bytes long starting at 0x%X\n",
+ EventLogRecord->RecordNumber, EventLogRecord->Length,
+ EventLogRecord->Length, Record);
+ Module = (LPWSTR)(EventLogRecord+1);
+ Computer = (LPWSTR)((PBYTE) Module + ((wcslen(Module) + 1) * sizeof(WCHAR)));
+ printf("\tGenerated by %ws from system %ws\n", Module, Computer);
+
+ TimeBuffer = ctime((time_t *)&(EventLogRecord->TimeGenerated));
+ if (TimeBuffer) {
+ printf("\tGenerated at %s", TimeBuffer);
+ }
+ else {
+ printf("\tGenerated time field is blank\n");
+ }
+ TimeBuffer = ctime((time_t *)&(EventLogRecord->TimeWritten));
+ if (TimeBuffer) {
+ printf("\tWritten at %s", TimeBuffer);
+ }
+ else {
+ printf("\tTime written field is blank\n");
+ }
+
+ printf("\tEvent Id = %d\n", EventLogRecord->EventID);
+ printf("\tEventType = ");
+ switch (EventLogRecord->EventType) {
+ case EVENTLOG_SUCCESS:
+ printf("Success\n");
+ break;
+ case EVENTLOG_ERROR_TYPE:
+ printf("Error\n");
+ break;
+ case EVENTLOG_WARNING_TYPE:
+ printf("Warning\n");
+ break;
+ case EVENTLOG_INFORMATION_TYPE:
+ printf("Information\n");
+ break;
+ case EVENTLOG_AUDIT_SUCCESS:
+ printf("Audit Success\n");
+ break;
+ case EVENTLOG_AUDIT_FAILURE:
+ printf("Audit Failure\n");
+ break;
+ default:
+ printf("Invalid value 0x%X\n", EventLogRecord->EventType);
+ }
+ printf("\t%d strings at offset 0x%X\n", EventLogRecord->NumStrings,
+ EventLogRecord->StringOffset);
+ printf("\t%d bytes of data at offset 0x%X\n", EventLogRecord->DataLength,
+ EventLogRecord->DataOffset);
+ }
+
+ if (FirstPiece) {
+ Record = StartOfFile + sizeof(ELF_LOGFILE_HEADER) + BufferLen -
+ FirstPiece;
+ }
+ else {
+ Record += EventLogRecord->Length;
+ }
+
+ LocalFree(EventLogRecord);
+ return(Record);
+}
+
+//
+// Dump a record, or all records, or n records
+//
+
+VOID
+record(
+ HANDLE hCurrentProcess,
+ HANDLE hCurrentThread,
+ DWORD dwCurrentPc,
+ PNTSD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+{
+ DWORD Pointer;
+ LOGFILE LogFile;
+ DWORD StartOfFile;
+ DWORD EndOfFile = 0;
+ DWORD RecordNumber = 0;
+
+ InitFunctionPointers(hCurrentProcess, lpExtensionApis);
+
+ //
+ // Evaluate the argument string to get the address of
+ // the record to dump.
+ //
+
+ if (lpArgumentString && *lpArgumentString) {
+ if (*lpArgumentString == '.') {
+ if (GetLogFileAddress(lpArgumentString+1, &LogFile) == 0) {
+ printf("Logfile %s not found\n", lpArgumentString+1);
+ return;
+ }
+ Pointer = ((DWORD) (LogFile.BaseAddress)) + LogFile.BeginRecord;
+ }
+ else if (*lpArgumentString == '#') {
+ RecordNumber = atoi(lpArgumentString + 1);
+ printf("Dumping records starting at record #%d\n", RecordNumber);
+ lpArgumentString = NULL;
+ }
+ else if (*lpArgumentString) {
+ Pointer = (lpGetExpressionRoutine)(lpArgumentString);
+ }
+ else {
+ printf("Invalid lead character 0x%02X\n", *lpArgumentString);
+ return;
+ }
+ }
+
+ if (!lpArgumentString || *lpArgumentString) {
+ if (GetLogFileAddress("system", &LogFile) == 0) {
+ printf("System Logfile not found\n");
+ return;
+ }
+ Pointer = ((DWORD) (LogFile.BaseAddress)) + LogFile.BeginRecord;
+ }
+
+ StartOfFile = (DWORD) LogFile.BaseAddress;
+ EndOfFile = (DWORD) LogFile.BaseAddress + LogFile.ActualMaxFileSize;
+
+ //
+ // Dump records starting wherever they told us to
+ //
+
+ while (Pointer < EndOfFile && Pointer && !(lpCheckControlCRoutine)()) {
+ Pointer = DumpRecord(Pointer, RecordNumber, StartOfFile, EndOfFile);
+ }
+
+
+ return;
+}
+
+//
+// Dump a single LogModule structure if it matches MatchName (NULL matches
+// all)
+//
+
+PLIST_ENTRY
+DumpLogModule(
+ HANDLE hCurrentProcess,
+ DWORD pLogModule,
+ LPWSTR MatchName
+ )
+{
+ LOGMODULE LogModule;
+ WCHAR ModuleName[MAX_NAME / sizeof(WCHAR)];
+
+ GET_DATA(pLogModule, &LogModule, sizeof(LogModule))
+ GET_DATA(LogModule.ModuleName, &ModuleName, MAX_NAME)
+
+ if (!MatchName || !wcsicmp(MatchName, ModuleName)) {
+ printf("\tModule Name %ws\n", ModuleName);
+ printf("\tModule Atom 0x%X\n", LogModule.ModuleAtom);
+ printf("\tPointer to LogFile 0x%X\n", LogModule.LogFile);
+ }
+
+ return (LogModule.ModuleList.Flink);
+}
+
+//
+// Dump selected, or all, LogModule structures
+//
+
+VOID
+logmodule(
+ HANDLE hCurrentProcess,
+ HANDLE hCurrentThread,
+ DWORD dwCurrentPc,
+ PNTSD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+{
+ DWORD pLogModule;
+ DWORD LogModuleAnchor;
+ LPWSTR wArgumentString = NULL;
+ ANSI_STRING AnsiString;
+ UNICODE_STRING UnicodeString;
+
+ InitFunctionPointers(hCurrentProcess, lpExtensionApis);
+ UnicodeString.Buffer = NULL;
+
+ //
+ // Evaluate the argument string to get the address of
+ // the logmodule to dump. If no parm, dump them all.
+ //
+
+ if (lpArgumentString && *lpArgumentString == '.') {
+ lpArgumentString++;
+ RtlInitAnsiString(&AnsiString, lpArgumentString);
+ RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE);
+ }
+ else if (lpArgumentString && *lpArgumentString) {
+ pLogModule = (lpGetExpressionRoutine)(lpArgumentString);
+ DumpLogModule(hCurrentProcess, pLogModule, NULL);
+ return;
+ }
+
+ LogModuleAnchor = (lpGetExpressionRoutine)("LogModuleHead");
+
+ GET_DATA(LogModuleAnchor, &pLogModule, sizeof(DWORD))
+
+ while (pLogModule != LogModuleAnchor && !(lpCheckControlCRoutine)()) {
+ pLogModule =
+ (DWORD) DumpLogModule(hCurrentProcess, pLogModule,
+ UnicodeString.Buffer);
+ if (!UnicodeString.Buffer) {
+ printf("\n");
+ }
+ }
+ if (UnicodeString.Buffer) {
+ RtlFreeUnicodeString(&UnicodeString);
+ }
+
+ return;
+}
+
+//
+// Dump a single LogFile structure if it matches MatchName (NULL matches
+// all)
+//
+
+PLIST_ENTRY
+DumpLogFile(
+ HANDLE hCurrentProcess,
+ DWORD pLogFile,
+ LPWSTR MatchName
+ )
+{
+ LOGFILE LogFile;
+ LPWSTR UnicodeName;
+
+ //
+ // Get the fixed part of the structure
+ //
+
+ GET_DATA(pLogFile, &LogFile, sizeof(LogFile))
+
+ //
+ // Get the Default module name
+ //
+
+ UnicodeName = GetUnicodeString(LogFile.LogModuleName);
+
+ //
+ // See if we're just looking for a particular one. If we are and
+ // this isn't it, bail out.
+ //
+
+ if (MatchName && wcsicmp(MatchName, UnicodeName)) {
+ LocalFree(UnicodeName);
+ return (LogFile.FileList.Flink);
+ }
+
+ //
+ // Otherwise print it out
+ //
+
+ printf("%ws", UnicodeName);
+ LocalFree(UnicodeName);
+
+ //
+ // Now the file name of this logfile
+ //
+
+ UnicodeName = GetUnicodeString(LogFile.LogFileName);
+ printf(" : %ws\n", UnicodeName);
+ LocalFree(UnicodeName);
+
+ if (LogFile.Notifiees.Flink == LogFile.Notifiees.Blink) {
+ printf("\tNo active ChangeNotifies on this log\n");
+ }
+ else {
+ printf("\tActive Change Notify! Dump of this list not implemented\n");
+ }
+
+ printf("\tReference Count: %d\n\tFlags: ", LogFile.RefCount);
+ if (LogFile.Flags == 0) {
+ printf("No flags set ");
+ }
+ else {
+ if (LogFile.Flags & ELF_LOGFILE_HEADER_DIRTY) {
+ printf("Dirty ");
+ }
+ if (LogFile.Flags & ELF_LOGFILE_HEADER_WRAP) {
+ printf("Wrapped ");
+ }
+ if (LogFile.Flags & ELF_LOGFILE_LOGFULL_WRITTEN) {
+ printf("Logfull Written ");
+ }
+ }
+ printf("\n");
+
+ printf("\tMax Files Sizes [Cfg:Curr:Next] 0x%X : 0x%X : 0x%X\n",
+ LogFile.ConfigMaxFileSize, LogFile.ActualMaxFileSize,
+ LogFile.NextClearMaxFileSize);
+
+ printf("\tRecord Numbers [Oldest:Curr] %d : %d\n",
+ LogFile.OldestRecordNumber, LogFile.CurrentRecordNumber);
+
+ printf("\tRetention period in days: %d\n", LogFile.Retention / 86400);
+
+ printf("\tBase Address: 0x%X\n", LogFile.BaseAddress);
+
+ printf("\tView size: 0x%X\n", LogFile.ViewSize);
+
+ printf("\tOffset of beginning record: 0x%X\n", LogFile.BeginRecord);
+
+ printf("\tOffset of ending record: 0x%X\n", LogFile.EndRecord);
+
+ return (LogFile.FileList.Flink);
+}
+
+//
+// Dump selected, or all, LogFile structures
+//
+
+VOID
+logfile(
+ HANDLE hCurrentProcess,
+ HANDLE hCurrentThread,
+ DWORD dwCurrentPc,
+ PNTSD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+{
+ DWORD pLogFile;
+ DWORD LogFileAnchor;
+ LPWSTR wArgumentString = NULL;
+ ANSI_STRING AnsiString;
+ UNICODE_STRING UnicodeString;
+ BOOL AllocateString = FALSE;
+
+ InitFunctionPointers(hCurrentProcess, lpExtensionApis);
+ UnicodeString.Buffer = NULL;
+
+ //
+ // Evaluate the argument string to get the address of
+ // the logfile to dump. If no parm, dump them all.
+ //
+
+ if (lpArgumentString && *lpArgumentString) {
+ if(*lpArgumentString == '.') {
+ lpArgumentString++;
+ RtlInitAnsiString(&AnsiString, lpArgumentString);
+ RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE);
+ }
+ else {
+ pLogFile = (lpGetExpressionRoutine)(lpArgumentString);
+ DumpLogFile(hCurrentProcess, pLogFile, NULL);
+ return;
+ }
+ }
+
+ LogFileAnchor = (lpGetExpressionRoutine)("LogFilesHead");
+
+ GET_DATA(LogFileAnchor, &pLogFile, sizeof(DWORD))
+
+ while (pLogFile != LogFileAnchor) {
+ pLogFile =
+ (DWORD) DumpLogFile(hCurrentProcess, pLogFile,
+ UnicodeString.Buffer);
+ if (!UnicodeString.Buffer) {
+ printf("\n");
+ }
+ }
+
+ if (UnicodeString.Buffer) {
+ RtlFreeUnicodeString(&UnicodeString);
+ }
+
+ return;
+}
+
+//
+// Dump a request packet structure
+//
+
+VOID
+request(
+ HANDLE hCurrentProcess,
+ HANDLE hCurrentThread,
+ DWORD dwCurrentPc,
+ PNTSD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+{
+ ELF_REQUEST_RECORD Request;
+ DWORD Pointer;
+ DWORD RecordSize;
+ WRITE_PKT WritePkt;
+ READ_PKT ReadPkt;
+ CLEAR_PKT ClearPkt;
+ BACKUP_PKT BackupPkt;
+ LPWSTR FileName;
+ CHAR Address[18];
+
+ InitFunctionPointers(hCurrentProcess, lpExtensionApis);
+
+ //
+ // Evaluate the argument string to get the address of
+ // the request packet to dump.
+ //
+
+ if (lpArgumentString && *lpArgumentString) {
+ Pointer = (lpGetExpressionRoutine)(lpArgumentString);
+ }
+ else {
+ printf("Must supply a request packet address\n");
+ return;
+ }
+
+ GET_DATA(Pointer, &Request, sizeof(ELF_REQUEST_RECORD))
+
+ switch (Request.Command ) {
+ case ELF_COMMAND_READ:
+ printf("\nRead packet\n");
+ GET_DATA(Request.Pkt.ReadPkt, &ReadPkt, sizeof(READ_PKT))
+ printf("\tLast Seek Position = %d\n", ReadPkt.LastSeekPos);
+ printf("\tLast Seek Record = %d\n", ReadPkt.LastSeekRecord);
+ printf("\tStart at record number %d\n", ReadPkt.RecordNumber);
+ printf("\tRead %d bytes into buffer at 0x%X\n",
+ ReadPkt.BufferSize, ReadPkt.Buffer);
+ if (ReadPkt.Flags & ELF_IREAD_UNICODE) {
+ printf("\tReturn in ANSI\n");
+ }
+ else {
+ printf("\tReturn in UNICODE\n");
+ }
+ printf("\tRead flags: ");
+ if (ReadPkt.ReadFlags & EVENTLOG_SEQUENTIAL_READ) {
+ printf("Sequential ");
+ }
+ if (ReadPkt.ReadFlags & EVENTLOG_SEEK_READ) {
+ printf("Seek ");
+ }
+ if (ReadPkt.ReadFlags & EVENTLOG_FORWARDS_READ) {
+ printf("Forward ");
+ }
+ if (ReadPkt.ReadFlags & EVENTLOG_BACKWARDS_READ) {
+ printf("Backwards ");
+ }
+ printf("\n");
+ break;
+
+ case ELF_COMMAND_WRITE:
+ printf("\nWrite packet\n");
+ if (Request.Flags == ELF_FORCE_OVERWRITE) {
+ printf("with ELF_FORCE_OVERWRITE enabled\n");
+ }
+ else {
+ printf("\n");
+ }
+ GET_DATA(Request.Pkt.WritePkt, &WritePkt, sizeof(WRITE_PKT))
+ RecordSize = (WritePkt.Datasize);
+ DumpRecord((DWORD)WritePkt.Buffer, 0, 0, 0);
+ break;
+
+ case ELF_COMMAND_CLEAR:
+ printf("\nClear packet\n");
+ GET_DATA(Request.Pkt.ClearPkt, &ClearPkt, sizeof(CLEAR_PKT))
+ FileName = GetUnicodeString(ClearPkt.BackupFileName);
+ printf("Backup filename = %ws\n", FileName);
+ LocalFree(FileName);
+ break;
+
+ case ELF_COMMAND_BACKUP:
+ printf("\nBackup packet\n");
+ GET_DATA(Request.Pkt.BackupPkt, &BackupPkt, sizeof(BACKUP_PKT))
+ FileName = GetUnicodeString(BackupPkt.BackupFileName);
+ printf("Backup filename = %ws\n", FileName);
+ LocalFree(FileName);
+ break;
+
+ case ELF_COMMAND_WRITE_QUEUED:
+ printf("\nQueued Write packet\n");
+ if (Request.Flags == ELF_FORCE_OVERWRITE) {
+ printf("with ELF_FORCE_OVERWRITE enabled\n");
+ }
+ else {
+ printf("\n");
+ }
+ printf("NtStatus = 0x%X\n", Request.Status);
+ break;
+
+ default:
+ printf("\nInvalid packet\n");
+ }
+
+ printf("\nLogFile for this packet:\n\n");
+ itoa((DWORD) Request.LogFile, Address, 16);
+ logfile(hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis,
+ Address);
+
+ printf("\nLogModule for this packet:\n\n");
+ itoa((DWORD)Request.Module, Address, 16);
+ logmodule(hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis,
+ Address);
+
+ return;
+}
+
+//
+// Online help
+//
+
+VOID
+help(
+ HANDLE hCurrentProcess,
+ HANDLE hCurrentThread,
+ DWORD dwCurrentPc,
+ PNTSD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+{
+ InitFunctionPointers(hCurrentProcess, lpExtensionApis);
+
+ printf("\nEventlog NTSD Extensions\n");
+
+ if (!lpArgumentString || *lpArgumentString == '\0' ||
+ *lpArgumentString == '\n' || *lpArgumentString == '\r') {
+ printf("\tlogmodule - dump a logmodule structure\n");
+ printf("\tlogfile - dump a logfile structure\n");
+ printf("\trequest - dump a request record\n");
+ printf("\trecord - dump a eventlog record\n");
+ printf("\n\tEnter help <cmd> for detailed help on a command\n");
+ }
+ else {
+ if (!stricmp(lpArgumentString, "logmodule")) {
+ printf("\tlogmodule <arg>, where <arg> can be one of:\n");
+ printf("\t\tno argument - dump all logmodule structures\n");
+ printf("\t\taddress - dump the logmodule at specified address\n");
+ printf("\t\t.string - dump the logmodule with name string\n");
+ }
+ else if (!stricmp(lpArgumentString, "logfile")) {
+ printf("\tlogfile <arg>, where <arg> can be one of:\n");
+ printf("\t\tno argument - dump all logfile structures\n");
+ printf("\t\taddress - dump the logfile at specified address\n");
+ printf("\t\t.string - dump the logfile with name string\n");
+ }
+ else if (!stricmp(lpArgumentString, "record")) {
+ printf("\trecord <arg>, where <arg> can be one of:\n");
+ printf("\t\tno argument - dump all records in system log\n");
+ printf("\t\taddress - dump records starting at specified address\n");
+ printf("\t\t.string - dump all records in the <string> log\n");
+ printf("\t\t#<nnn> - dumps records starting at nnn in system log\n");
+ printf("\t\t#<nnn> .string - dumps records starting at nnn in <string> log\n");
+ }
+ else if (!stricmp(lpArgumentString, "request")) {
+ printf("\trequest - dump the request record at specified address\n");
+ }
+ else {
+ printf("\tInvalid command [%s]\n", lpArgumentString);
+ }
+ }
+}
diff --git a/private/eventlog/ntsdexts/elfexts.def b/private/eventlog/ntsdexts/elfexts.def
new file mode 100644
index 000000000..bfe2048d3
--- /dev/null
+++ b/private/eventlog/ntsdexts/elfexts.def
@@ -0,0 +1,10 @@
+LIBRARY NTSDEXTS
+
+DESCRIPTION 'Eventlog Default NTSD Extensions'
+
+EXPORTS
+ help
+ logmodule
+ logfile
+ request
+ record
diff --git a/private/eventlog/ntsdexts/elfexts.rc b/private/eventlog/ntsdexts/elfexts.rc
new file mode 100644
index 000000000..e351edd4b
--- /dev/null
+++ b/private/eventlog/ntsdexts/elfexts.rc
@@ -0,0 +1,9 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Eventlog Symbolic Debugger Extensions"
+#define VER_INTERNALNAME_STR "elfexts\0"
+
+#include "common.ver"
diff --git a/private/eventlog/ntsdexts/makefile b/private/eventlog/ntsdexts/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/eventlog/ntsdexts/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/eventlog/ntsdexts/sources b/private/eventlog/ntsdexts/sources
new file mode 100644
index 000000000..34f6a132d
--- /dev/null
+++ b/private/eventlog/ntsdexts/sources
@@ -0,0 +1,40 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Dan Hinsley (DanHi) 22-Mar-1993
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=eventlog
+MINORCOMP=ntsdexts
+
+TARGETNAME=elfexts
+TARGETPATH=obj
+TARGETTYPE=DYNLINK
+TARGETLIBS=\
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\libc.lib
+
+INCLUDES=..;..\server;..\..\inc;
+SOURCES=elfexts.c \
+ elfexts.rc
+
+UMTYPE=console
+DLLBASE=0x66100000
diff --git a/private/eventlog/readme.txt b/private/eventlog/readme.txt
new file mode 100644
index 000000000..34c9b1d22
--- /dev/null
+++ b/private/eventlog/readme.txt
@@ -0,0 +1,103 @@
+This is a description of the contents of each file in the eventlog
+project.
+Ignore the test and enumsvc directories. They contain test programs.
+
+
+eventlog
+--------
+dirs The directories to build.
+
+elf.acf The ACF file for RPC.
+
+elf.h Generated by MIDL using elf.idl.
+
+elf.idl The IDL descriptions of the APIs and the data structures.
+
+elfcommn.h Common defines/declarations used by the server and the client
+ sides.
+
+event.h Data structures that should be placed in a common location in the
+ nt source tree.
+
+imports.h
+imports.idl Import files used by MIDL.
+
+makefil0 Makefile to make the RPC client and server side files.
+
+
+elfclnt
+-------
+
+apistub.c The stubs for the ELF APIs.
+
+elf_c.c Generated by MIDL.
+elf_x.c
+
+elfapi.def The APIs that are exported in the DLL.
+
+elfclntp.h Private header file.
+
+rpcbind.c Bind routines that are called by the RPC run times.
+
+makefile
+sources
+
+
+server
+------
+
+config.c Routines that deal with the registry and configuration for the
+ service. Contains the main registry monitor thread.
+
+control.c Routines that deal with control calls from the service controller.
+
+elf_s.c Generated by MIDL.
+elf_y.c
+
+elfapi.c The server side of the APIs.
+
+elfcfg.h Header file for configuration related data.
+
+elfdata.c Global data.
+
+elfdebug.c DEBUG routines. These should go away in the final version.
+
+elfdef.h Data structure definitions and #defines used in the service.
+
+elfextrn.h Extern declarations of the global data.
+
+elflpc.c Routines that deal with the LPC port and with fielding packets
+ from the device driver.
+
+elflpc.h Header file for elflpc.c.
+
+elfproto.h Procedure prototypes.
+
+elfrpc.c RPC-related routines to clean up the context handle.
+
+elfutil.c Various low-level utility routines.
+
+eventlog.c Main entry point for service. All the initialization code.
+
+eventp.h Private header file for server end.
+
+file.c Routines to manage the log files.
+
+memory.c Alloc/Free.
+
+operate.c All the low-level routines to perform the API requests.
+
+terminat.c Routines for terminating the service, and cleaning up.
+
+makefile
+sources
+
+
+winbase - these changes will go into \nt\private\windows\base\client
+-------
+
+base.def Define the Win32 event logging APIs.
+
+eventapi.c Entry points for the APIs.
+
+sources
diff --git a/private/eventlog/server/alert.c b/private/eventlog/server/alert.c
new file mode 100644
index 000000000..3751b91b2
--- /dev/null
+++ b/private/eventlog/server/alert.c
@@ -0,0 +1,147 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ALERT.C
+
+Abstract:
+
+ This file contains the routine that sends the alert to the admin.
+
+Author:
+
+ Rajen Shah (rajens) 28-Aug-1991
+
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <string.h>
+#include <lmalert.h> // LAN Manager alert structures
+
+
+
+BOOL
+SendAdminAlert (
+ ULONG MessageID,
+ ULONG NumStrings,
+ UNICODE_STRING *pStrings
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine raises an ADMIN alert with the message specified.
+
+
+Arguments:
+
+ MessageID - Message ID.
+ NumStrings - Number of replacement strings.
+ pStrings - Array of UNICODE_STRING Replacement strings.
+
+Return Value:
+
+ NONE
+
+Note:
+
+ This routine dynamically links to netapi32.dll to avoid a build
+ dependency.
+
+--*/
+{
+
+ typedef DWORD (*ALERT_FNC_PTR)(
+ IN LPTSTR AlertEventName,
+ IN LPVOID VariableInfo,
+ IN DWORD VariableInfoSize,
+ IN LPTSTR ServiceName
+ );
+
+
+ NET_API_STATUS NetStatus;
+
+ BYTE AlertBuffer[ELF_ADMIN_ALERT_BUFFER_SIZE + sizeof(ADMIN_OTHER_INFO)];
+ ADMIN_OTHER_INFO UNALIGNED *VariableInfo = (PADMIN_OTHER_INFO) AlertBuffer;
+
+ DWORD DataSize;
+ DWORD i;
+ LPWSTR pReplaceString;
+
+ HANDLE dllHandle;
+ ALERT_FNC_PTR AlertFunction;
+
+ //
+ // Dynamically link to netapi.dll. If it's not there just return. Return
+ // TRUE so we won't try to send this alert again.
+ //
+
+ dllHandle = LoadLibraryA("NetApi32.Dll");
+ if ( dllHandle == INVALID_HANDLE_VALUE ) {
+ ElfDbgPrint(("[ELF] Failed to load DLL NetApi32.Dll: %ld\n",
+ GetLastError()));
+ return(TRUE);
+ }
+
+ //
+ // Get the address of the service's main entry point. This
+ // entry point has a well-known name.
+ //
+
+ AlertFunction = (ALERT_FNC_PTR)GetProcAddress(
+ dllHandle,
+ "NetAlertRaiseEx"
+ );
+ if (AlertFunction == NULL ) {
+ ElfDbgPrint(("[ELF] Can't find entry NetAlertRaiseEx in NetApi32.Dll: %ld\n",
+ GetLastError()));
+ return(TRUE);
+
+ }
+
+ VariableInfo->alrtad_errcode = MessageID;
+ VariableInfo->alrtad_numstrings = NumStrings;
+
+ pReplaceString = (LPWSTR)(AlertBuffer + sizeof(ADMIN_OTHER_INFO));
+
+ //
+ // Copy over the replacement strings
+ //
+
+ for (i = 0; i < NumStrings; i++) {
+ RtlMoveMemory(pReplaceString, pStrings[i].Buffer,
+ pStrings[i].MaximumLength);
+ pReplaceString = (LPWSTR) ((PBYTE) pReplaceString +
+ pStrings[i].MaximumLength);
+ }
+
+ DataSize = (PBYTE) pReplaceString - (PBYTE) AlertBuffer;
+
+ NetStatus = (*AlertFunction)(ALERT_ADMIN_EVENT, AlertBuffer, DataSize,
+ wname_Eventlogsvc);
+
+ if (NetStatus != NERR_Success) {
+ ElfDbgPrint(("[ELF] NetAlertRaiseEx failed with %d\n", NetStatus));
+ ElfDbgPrint(("[ELF] Attempted to raise alert %d\n", MessageID));
+
+ //
+ // Probably just not started yet, try again later
+ //
+
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
diff --git a/private/eventlog/server/cairo.c b/private/eventlog/server/cairo.c
new file mode 100644
index 000000000..200049be1
--- /dev/null
+++ b/private/eventlog/server/cairo.c
@@ -0,0 +1,471 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ CAIRO.C
+
+Abstract:
+
+ This file contains Cairo alert extensions to the NT event log.
+
+Author:
+
+ Mark Blanford (markbl) 02-Nov-1994 New routines:
+
+ GetSourceAlertValuesFromRegistry
+ RaiseCairoAlert
+ TestSeverityFilter
+
+ With this check-in, it is possible for an NT event written to the 'System'
+ log to be raised as a Cairo alert. Further, it is possible for any
+ 'System' log source to have the NT events it writes be raised as Cairo
+ alerts (subject to a filter) by simply supplying two key values:
+ 'AlertCategory' & 'SeverityFilter' with their source name key under the
+ ...\EventLog\System portion of the registry.
+
+ The 'AlertCategory' key value specifies the alert category value used in
+ the alerts created from the source's NT events.
+
+ The 'SeverityFilter' key value is a bit mask specifying the severity of
+ NT events to be converted to Cairo alerts. Note: alert severity values are
+ a superset of the NT event types information, warning & error. For
+ example: assume the "Srv" source has specified alert category and severity
+ key values, and the severity value, a bit mask, is set to warning | error.
+ The NT events "Srv" subsequently writes to the 'System' log of NT event
+ type warning or error will be distributed as Cairo alerts. "Srv" NT events
+ with NT event types other than warning or error will not be raised as
+ Cairo alerts.
+
+ All Cairo alerts generated as a result of this conversion, are raised to
+ the local computer distributor.
+
+[Environment:]
+
+ User Mode - Win32, except for NTSTATUS returned by some functions.
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <ole2.h>
+#include <oleext.h>
+#include <sysmgmt.h>
+#include <alertapi.h>
+
+GUID CLSID_CNTEventReport = {
+ 0x43750e20,
+ 0xfa68,
+ 0x11cd,
+ { 0x84, 0xb7, 0x00, 0xaa, 0x00, 0x4a, 0x56, 0xe1 }
+};
+
+static WCHAR wszAlertCategory[] = L"AlertCategory";
+static WCHAR wszSeverityFilter[] = L"SeverityFilter";
+
+BOOL IsValidSeverityFilter(SHORT sSeverity);
+
+
+/*++
+
+Routine Description:
+
+ Fetch "...\EventLog\System\<SourceName>" 'AlertCategory' and
+ 'SeverityFilter' registry values, if they exist.
+
+Arguments:
+
+ hKeyLogFile - Registry handle to '...\EventLog\<LogName>' where
+ <LogName> is likely to be 'System'.
+ pswszSourceName - Source name registered to report events to the log
+ indicated above.
+ psAlertCategory - Returned category value.
+ psSeverityFilter - Returned severity value.
+
+Return Value:
+
+ BOOL - TRUE if the category,severity filter was successfully read;
+ FALSE otherwise.
+
+Note:
+
+--*/
+
+BOOL
+GetSourceAlertFilterFromRegistry(
+ HANDLE hKeyLogFile,
+ UNICODE_STRING * pswszSourceName,
+ SHORT * psAlertCategory,
+ SHORT * psSeverityFilter)
+{
+#define KEY_VALUE_FULL_INFO_SIZE 1024 // BUGBUG : need proper size
+
+ BOOL fRet = FALSE;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING SubKeyName;
+ HANDLE hKeySource;
+ BYTE Buffer[KEY_VALUE_FULL_INFO_SIZE];
+ PKEY_VALUE_FULL_INFORMATION KeyValue = (PKEY_VALUE_FULL_INFORMATION)Buffer;
+ ULONG Index = 0;
+ ULONG ActualSize;
+ SHORT sAlertCategory, sSeverityFilter;
+
+ ASSERT(pswszSourceName != NULL);
+ ASSERT(psAlertCategory != NULL);
+ ASSERT(psSeverityFilter != NULL);
+
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ pswszSourceName,
+ OBJ_CASE_INSENSITIVE,
+ hKeyLogFile,
+ NULL);
+
+ //
+ // Read category,severity filter values, if they exist, for this source.
+ //
+
+ Status = NtOpenKey(&hKeySource, KEY_READ, &ObjectAttributes);
+
+ if (NT_SUCCESS(Status))
+ {
+ //
+ // Fetch alert category value, if it exists.
+ //
+
+ RtlInitUnicodeString(&SubKeyName, wszAlertCategory);
+
+ Status = NtQueryValueKey(hKeySource,
+ &SubKeyName,
+ KeyValueFullInformation,
+ KeyValue,
+ KEY_VALUE_FULL_INFO_SIZE,
+ &ActualSize);
+
+ if (NT_SUCCESS(Status))
+ {
+ sAlertCategory = *((PSHORT)(Buffer + KeyValue->DataOffset));
+
+ //
+ // Fetch severity filter value, if it exists.
+ //
+
+ RtlInitUnicodeString(&SubKeyName, wszSeverityFilter);
+
+ Status = NtQueryValueKey(hKeySource,
+ &SubKeyName,
+ KeyValueFullInformation,
+ KeyValue,
+ KEY_VALUE_FULL_INFO_SIZE,
+ &ActualSize);
+
+ if (NT_SUCCESS(Status))
+ {
+ sSeverityFilter = *((PSHORT)(Buffer + KeyValue->DataOffset));
+
+ //
+ // Is the severity valid?
+ //
+
+ if (!IsValidSeverityFilter(sSeverityFilter))
+ {
+ ElfDbgPrintNC((
+ "[ELF] Invalid severity filter (%02x) for " \
+ "source \"%ws\"\n",
+ sSeverityFilter,
+ pswszSourceName->Buffer));
+ return(FALSE);
+ }
+ }
+ }
+
+ if (NT_SUCCESS(Status))
+ {
+ *psAlertCategory = sAlertCategory;
+ *psSeverityFilter = sSeverityFilter;
+ fRet = TRUE;
+ }
+
+ Status = NtClose(hKeySource);
+ ASSERT(NT_SUCCESS(Status));
+ }
+ else
+ {
+ ElfDbgPrintNC((
+ "[ELF] Unable to open source key %ws to fetch alert " \
+ "filter (%x)\n",
+ pswszSourceName->Buffer,
+ Status));
+ }
+
+ return(fRet);
+}
+
+
+/*++
+
+Routine Description:
+
+ Bit-wise compare the passed NT event type & alert severity.
+
+Arguments:
+
+ NTEventType - NT event type.
+ sSeverityFilter - Alert severity.
+
+Return Value:
+
+ BOOL - TRUE if NTEventType has a bit in common with sSeverityFilter;
+ FALSE otherwise.
+
+Note:
+
+--*/
+
+BOOL
+TestFilter(WORD NTEventType, SHORT sSeverityFilter)
+{
+ //
+ // Mask off all bits but the alert severities.
+ //
+
+ if (((ALERTSEVERITY_FATAL_ERROR |
+ ALERTSEVERITY_SEVERE_ERROR |
+ ALERTSEVERITY_ERROR |
+ ALERTSEVERITY_WARNING |
+ ALERTSEVERITY_INFORMATION) & sSeverityFilter) & NTEventType)
+ {
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+/*++
+
+Routine Description:
+
+ Test the severity passed for validity. No bits other than the severity
+ mask must be set and the severity must be non-zero.
+
+Arguments:
+
+ sSeverity - Tested severity.
+
+Return Value:
+
+ BOOL - TRUE if the test succeeds;
+ FALSE otherwise.
+
+Note:
+
+--*/
+
+BOOL
+IsValidSeverityFilter(SHORT sSeverity)
+{
+ //
+ // Insure the only bits set are the severity mask.
+ //
+
+ if (!(sSeverity & ~(ALERTSEVERITY_FATAL_ERROR |
+ ALERTSEVERITY_SEVERE_ERROR |
+ ALERTSEVERITY_ERROR |
+ ALERTSEVERITY_WARNING |
+ ALERTSEVERITY_INFORMATION)))
+ {
+ //
+ // Is a severity specified at all?
+ //
+
+ return(sSeverity ? TRUE : FALSE);
+ }
+
+ return(FALSE);
+}
+
+
+/*++
+
+Routine Description:
+
+ Convert & raise the NT event passed as a Cairo alert to the local
+ computer distributor.
+
+Arguments:
+
+ pLogModule - Specific log module. This structure supplies the alert
+ category value & the log module name.
+ pEventLogRecord - The NT event to be converted & raised as an alert.
+
+Return Value:
+
+ HRESULT -
+
+Note:
+
+--*/
+
+HRESULT
+RaiseCairoAlert(PLOGMODULE pLogModule, EVENTLOGRECORD * pEventLogRecord)
+{
+ HRESULT hr = S_OK;
+ BSTR bstrLogName = NULL;
+ WCHAR * pwszTmp;
+ int i;
+ ALERTREPORTRECORD alertreport;
+ VARIANT avar[2];
+ DISPID aid[2];
+ WCHAR ** apwszInserts = NULL;
+ HINSTANCE hInstance;
+
+
+ do
+ {
+ //
+ // ALERTSYS.DLL & the ReportAlert entry point is linked to by hand
+ // in the eventlog initialization code (eventlog.c). If this global
+ // handle is NULL, the link attempt failed.
+ //
+
+ if (ghAlertSysDll == NULL)
+ {
+ //
+ // BUGBUG : Log an error?
+ //
+
+ return(E_FAIL);
+ }
+
+ //
+ // Initialize mandatory properties.
+ //
+
+ INITIALIZE_ALERTREPORTRECORD(alertreport,
+ pLogModule->AlertCategory, // Alert category
+ pEventLogRecord->EventType, // Alert severity
+ -1, // No title message no.
+ pEventLogRecord->EventID, // Description message no.
+ NULL, // No component ID
+ (WCHAR *)((BYTE *)pEventLogRecord + // Source name
+ sizeof(EVENTLOGRECORD)));
+
+ //
+ // Alert report class
+ //
+
+ alertreport.ReportClassID = &CLSID_CNTEventReport;
+
+ //
+ // Properties specific to NT event reports...
+ //
+ // Event category
+ //
+
+ aid[0] = DISPID_NTEventReport_EventCategory;
+ VariantInit(&avar[0]);
+ avar[0].vt = VT_I2;
+ avar[0].iVal = pEventLogRecord->EventCategory;
+
+ //
+ // Log module name
+ //
+
+ bstrLogName = SysAllocString(pLogModule->LogFile->
+ LogModuleName->Buffer);
+
+ if (bstrLogName == NULL)
+ {
+ ElfDbgPrintNC((
+ "[ELF RaiseCairoAlert] failed to allocate log name bstr\n"));
+ hr = E_OUTOFMEMORY;
+ break;
+ }
+
+ aid[1] = DISPID_NTEventReport_LogFile;
+ VariantInit(&avar[1]);
+ avar[1].vt = VT_BSTR;
+ avar[1].bstrVal = bstrLogName;
+
+ //
+ // Description inserts; optional.
+ //
+
+ if (pEventLogRecord->NumStrings > 0)
+ {
+ apwszInserts = (WCHAR **)ElfpAllocateBuffer(sizeof(WCHAR *) *
+ pEventLogRecord->NumStrings);
+
+ if (apwszInserts == NULL)
+ {
+ ElfDbgPrintNC((
+ "[ELF RaiseCairoAlert] failed to allocate insert " \
+ "string\n"));
+ hr = E_OUTOFMEMORY;
+ break;
+ }
+
+ for (i = 0, pwszTmp = (WCHAR *)((BYTE *)pEventLogRecord +
+ pEventLogRecord->StringOffset);
+ (pwszTmp != NULL) && (i < pEventLogRecord->NumStrings); i++)
+ {
+ apwszInserts[i] = pwszTmp;
+ pwszTmp += wcslen(pwszTmp) + 1;
+ }
+
+ alertreport.cDescrMessageInserts = i;
+ alertreport.DescrMessageInserts = apwszInserts;
+ }
+
+ alertreport.cAdditionalArguments = 2;
+ alertreport.AdditionalArgumentNames = aid;
+ alertreport.AdditionalArguments = avar;
+
+ //
+ // Alert data; optional, as well.
+ //
+
+ if (pEventLogRecord->DataLength > 0)
+ {
+ alertreport.cBytesAlertData = pEventLogRecord->DataLength;
+ alertreport.AlertData = (BYTE *)pEventLogRecord +
+ pEventLogRecord->DataOffset;
+ }
+
+ //
+ // Finally, raise the alert.
+ //
+
+ hr = gpfReportAlert(&alertreport, RA_REPORT);
+
+ if (FAILED(hr))
+ {
+ ElfDbgPrintNC((
+ "[ELF RaiseCairoAlert] ReportAlert failed HRESULT(%x)\n",
+ hr));
+ break;
+ }
+
+ } while (0);
+
+ // Cleanup
+
+ if (bstrLogName != NULL)
+ {
+ SysFreeString(bstrLogName);
+ }
+ if (apwszInserts != NULL)
+ {
+ ElfpFreeBuffer(apwszInserts);
+ }
+
+ return(hr);
+}
diff --git a/private/eventlog/server/config.c b/private/eventlog/server/config.c
new file mode 100644
index 000000000..ebf45b8b0
--- /dev/null
+++ b/private/eventlog/server/config.c
@@ -0,0 +1,1007 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ CONFIG.C
+
+Abstract:
+
+ This file contains the routines that walk the configuration registry.
+
+Author:
+
+ Rajen Shah (rajens) 1-Jul-1991
+
+
+Revision History:
+
+ 29-Aug-1994 Danl
+ We no longer grow log files in place. Therefore, the MaxSize value
+ in the registery ends up being advisory only. We don't try to reserve
+ that much memory at init time. So it could happen that when we need
+ a larger file size that we may not have enough memory to allocate
+ MaxSize bytes.
+ 28-Mar-1994 Danl
+ ReadRegistryInfo: LogFileInfo->LogFileName wasn't getting updated
+ when using the default (generated) LogFileName.
+ 16-Mar-1994 Danl
+ Fixed Memory Leaks in ReadRegistryInfo(). Call to
+ RtlDosPathNameToNtPathName allocates memory that wasn't being free'd.
+ 03-Mar-1995 MarkBl
+ Added GuestAccessRestriction flag initialization in ReadRegistryInfo.
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <elfcfg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+
+//
+// STRUCTURES
+//
+
+//
+// This structure contains all the information used to setup and
+// for listening to registry changes in the eventlog tree.
+//
+typedef struct _REG_MONITOR_INFO {
+ HANDLE NotifyEventHandle;
+ DWORD Timeout;
+ HANDLE WorkItemHandle;
+} REG_MONITOR_INFO, *LPREG_MONITOR_INFO;
+
+//
+// GLOBALS
+//
+ REG_MONITOR_INFO GlRegMonitorInfo;
+
+//
+// LOCAL FUNCTIONS
+//
+DWORD
+ElfRegistryMonitor (
+ LPVOID pParms,
+ DWORD dwWaitStatus
+ );
+
+BOOL
+ElfSetupMonitor(
+ LPREG_MONITOR_INFO pMonitorInfo
+ );
+
+
+
+
+VOID
+ProcessChange (
+ HANDLE hLogFile,
+ PUNICODE_STRING ModuleName,
+ PUNICODE_STRING LogFileName,
+ ULONG MaxSize,
+ ULONG Retention,
+ ULONG GuestAccessRestriction
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by ProcessRegistryChanges for each module.
+
+Arguments:
+
+
+
+Return Value:
+
+ None
+
+Note:
+
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLOGMODULE pModule;
+ PLOGFILE pLogFile;
+ ULONG Size;
+ PVOID BaseAddress;
+ PUNICODE_STRING pFileNameString;
+ LPWSTR FileName;
+ PVOID FreeAddress;
+
+
+ pModule = GetModuleStruc (ModuleName);
+
+ //
+ // If this module didn't exist, this was a brand new log file and
+ // we need to create all the structures
+ //
+
+ if (pModule == ElfDefaultLogModule &&
+ wcscmp(ModuleName->Buffer, ELF_DEFAULT_MODULE_NAME)) {
+ Status = SetUpDataStruct(LogFileName, MaxSize, Retention,
+ GuestAccessRestriction, ModuleName, hLogFile, ElfNormalLog);
+ return;
+ }
+
+ //
+ // Update values
+ //
+
+ pLogFile = pModule->LogFile;
+ pLogFile->Retention = Retention;
+
+ //
+ // Check to see if the name has changed. If it has, and the log
+ // hasn't been used yet, then use the new name. Be sure to free
+ // memory that was used for the old name.
+ //
+
+ if ((wcscmp(pLogFile->LogFileName->Buffer, LogFileName->Buffer) != 0) &&
+ (pLogFile->BeginRecord == pLogFile->EndRecord)) {
+
+ pFileNameString = ElfpAllocateBuffer(
+ sizeof(UNICODE_STRING) +
+ LogFileName->MaximumLength);
+
+ if (pFileNameString != NULL) {
+ FileName = (LPWSTR)(pFileNameString+1);
+ wcscpy(FileName, LogFileName->Buffer);
+ RtlInitUnicodeString(pFileNameString, FileName);
+
+ ElfpFreeBuffer(pLogFile->LogFileName);
+ pLogFile->LogFileName = pFileNameString;
+ }
+ }
+
+ //
+ // The log file can only be grown dynamically. To shrink it,
+ // it has to be cleared.
+ //
+
+
+ if (pLogFile->ConfigMaxFileSize < ELFFILESIZE(MaxSize)) {
+
+ /*
+ Description of recent changes. Problem and Solution:
+ A couple of problems exist. (1) There is no error
+ checking if memory can't be allocated or mapped, and
+ therefore, no error paths exist for handling these
+ situations. (2) Now that the eventlog is in services.exe
+ there isn't a good way to synchronize memory allocations.
+
+ Solution:
+ I considered having some utility routines for managing
+ memory in the eventlog. These would attempt to
+ extend a reserved block, or get a new reserved block.
+ However, there are so many places where that could fail,
+ it seemed very cumbersome to support the reserved blocks.
+ So the current design only deals with mapped views.
+ The ConfigMaxFileSize is only used to limit the size of
+ the mapped view, and doesn't reserve anything. This
+ means you are not guaranteed to be operating with a file as
+ large as the MaxSize specified in the registry. But then,
+ you weren't guarenteed that it would even work with the
+ original design.
+ */
+
+ pLogFile->ConfigMaxFileSize = ELFFILESIZE(MaxSize);
+ pLogFile->NextClearMaxFileSize = ELFFILESIZE(MaxSize);
+
+ }
+ else if (pLogFile->ConfigMaxFileSize > ELFFILESIZE(MaxSize)) {
+
+ //
+ // They're shrinking the size of the log file.
+ // Next time we clear the log file, we'll use the new size
+ // and new retention.
+ //
+
+ pLogFile->NextClearMaxFileSize = ELFFILESIZE(MaxSize);
+
+ }
+
+ //
+ // Now see if they've added any new modules for this log file
+ //
+
+ SetUpModules(hLogFile, pLogFile, TRUE);
+
+ return;
+}
+
+
+
+VOID
+ProcessRegistryChanges (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes that changes that have occurred in the
+ eventlog node. It does this by rescanning the whole Eventlog node
+ and then comparing with what it has as the current configuration.
+
+Arguments:
+
+ NONE.
+
+Return Value:
+
+ NONE
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hLogFile;
+ UNICODE_STRING SubKeyName;
+ ULONG Index = 0;
+ BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
+ PKEY_NODE_INFORMATION KeyBuffer = (PKEY_NODE_INFORMATION) Buffer;
+ ULONG ActualSize;
+ LOG_FILE_INFO LogFileInfo;
+ PWCHAR SubKeyString;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ ElfDbgPrint(("[ELF] ProcessRegistryChanges\n"));
+
+ //
+ // Take the global resource so that nobody is making changes or
+ // using the existing configured information.
+ //
+
+ GetGlobalResource (ELF_GLOBAL_SHARED);
+
+ //
+ // See if the Debug flag changed
+ //
+
+ RtlInitUnicodeString(&SubKeyName, VALUE_DEBUG);
+
+ NtQueryValueKey(hEventLogNode, &SubKeyName,
+ KeyValueFullInformation, KeyBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ElfDebug);
+
+
+ //
+ // Loop thru the subkeys under Eventlog and set up each logfile
+ //
+
+ while (NT_SUCCESS(Status)) {
+
+ Status = NtEnumerateKey(hEventLogNode, Index++, KeyNodeInformation,
+ KeyBuffer, ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // It turns out the Name isn't null terminated, so we need
+ // to copy it somewhere and null terminate it before we use it
+ //
+
+ SubKeyString = ElfpAllocateBuffer(KeyBuffer->NameLength +
+ sizeof (WCHAR));
+ if (!SubKeyString) {
+
+ //
+ // No one to notify, just give up till next time.
+ //
+
+ ReleaseGlobalResource();
+ return;
+ }
+
+ memcpy(SubKeyString, KeyBuffer->Name, KeyBuffer->NameLength);
+ SubKeyString[KeyBuffer->NameLength / sizeof(WCHAR)] = L'\0' ;
+
+ //
+ // Open the node for this logfile and extract the information
+ // required by SetupDataStruct, and then call it.
+ //
+
+ RtlInitUnicodeString(&SubKeyName, SubKeyString);
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ hEventLogNode,
+ NULL
+ );
+
+ Status = NtOpenKey(&hLogFile, KEY_READ | KEY_SET_VALUE,
+ &ObjectAttributes);
+
+ //
+ // Should always succeed since I just enum'ed it, but if it
+ // doesn't, just skip it
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfpFreeBuffer(SubKeyString);
+ Status = STATUS_SUCCESS; // to keep the enum going
+ continue;
+
+ }
+
+ Status = ReadRegistryInfo(hLogFile, &SubKeyName, & LogFileInfo);
+
+ if (NT_SUCCESS(Status)) {
+ //
+ // Now process any changes. Any errors are dealt with
+ // in ProcessChange
+ //
+
+ ProcessChange (
+ hLogFile,
+ &SubKeyName,
+ LogFileInfo.LogFileName,
+ LogFileInfo.MaxFileSize,
+ LogFileInfo.Retention,
+ LogFileInfo.GuestAccessRestriction
+ );
+
+ //
+ // Free the buffer that was allocated in ReadRegistryInfo.
+ //
+ ElfpFreeBuffer(LogFileInfo.LogFileName);
+ }
+
+ ElfpFreeBuffer(SubKeyString);
+ NtClose(hLogFile);
+ }
+ }
+
+ //
+ // Release the global resource.
+ //
+
+ ReleaseGlobalResource();
+
+
+} // ProcessRegistryChanges
+
+
+
+
+DWORD
+ElfRegistryMonitor (
+ LPVOID pParms,
+ DWORD dwWaitStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This is the entry point for the thread that will monitor changes in
+ the registry. If anything changes, it will have to scan the change
+ and then make the appropriate changes to the data structures in the
+ service to reflect the new information.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ LPREG_MONITOR_INFO pMonitorInfo=(LPREG_MONITOR_INFO)pParms;
+
+ ElfDbgPrint(("[ELF] Inside registry monitor thread\n"));
+
+
+ if (GetElState() == STOPPING) {
+ //
+ // If the eventlog is shutting down, then we need
+ // to terminate this thread.
+ //
+ ElfDbgPrint(("[ELF] ElfRegistryMonitor - Shutdown\n"));
+
+ //
+ // Close the registry handle and registry event handle.
+ //
+ NtClose( hEventLogNode);
+ CloseHandle(pMonitorInfo->NotifyEventHandle);
+
+ //===============================================================
+ // When we return from this call, the service controller will
+ // decrement our reference count to 0 and unload the DLL.
+ // So we can't exit until all the rest of the eventlog has shutdown.
+ // This thread will perform the final cleanup for the eventlog.
+ //
+ ElfpCleanUp(EventFlags);
+
+ //
+ // We should actually return here so that the DLL gets unloaded.
+ // However, RPC has a problem in that it might still call our
+ // context rundown routine even though we unregistered our interface.
+ // So we exit thread instead. This keeps our Dll loaded.
+ //
+ ExitThread(0);
+ }
+ if (dwWaitStatus == STATUS_TIMEOUT) {
+
+ ElfDbgPrint(("[ELF] Timer popped, running queued list\n"));
+
+ //
+ // Timer popped, try running the list
+ //
+
+ if (!IsListEmpty(&QueuedEventListHead)) {
+
+ //
+ // There are things queued up to write, do it
+ //
+
+ WriteQueuedEvents();
+
+ }
+
+ //
+ // Don't wait again
+ //
+
+ pMonitorInfo->Timeout = INFINITE;
+
+ }
+ else if (dwWaitStatus == WAIT_OBJECT_0) {
+ ElfDbgPrint(("[ELF] ElfRegistryMonitor - Notification\n"));
+ ProcessRegistryChanges ();
+ }
+ else {
+ ElfDbgPrint(("[ELF] WorkItemCallback returned %d\n",
+ dwWaitStatus));
+ }
+
+ if (!ElfSetupMonitor(pMonitorInfo)) {
+ ElfDbgPrint(("[ELF] ElfSetupMonitor Failed! Can't listen for Reg Changes\n"));
+ }
+
+ ElfDbgPrint(("[ELF] ElfRegistryMonitor - returning\n"));
+
+ return(0);
+
+} // ElfRegistryMonitor
+
+
+
+DWORD
+InitNotify(
+ PVOID pData,
+ DWORD dwWaitStatus
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS NtStatus = STATUS_SUCCESS;
+ DWORD status = NO_ERROR;
+ DWORD Buffer;
+ PVOID pBuffer = & Buffer;
+ LPREG_MONITOR_INFO pMonitorInfo;
+
+ static IO_STATUS_BLOCK IoStatusBlock;
+
+ UNREFERENCED_PARAMETER(dwWaitStatus);
+
+ ElfDbgPrint(("[ELF]In InitNotify Routine\n"));
+
+ pMonitorInfo = (LPREG_MONITOR_INFO)pData;
+
+ NtStatus = NtNotifyChangeKey (
+ hEventLogNode,
+ pMonitorInfo->NotifyEventHandle,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ REG_NOTIFY_CHANGE_LAST_SET |
+ REG_NOTIFY_CHANGE_NAME,
+ TRUE,
+ pBuffer,
+ 1,
+ TRUE // return and wait on event
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+ status = RtlNtStatusToDosError(NtStatus);
+ }
+ ElfDbgPrint(("[ELF]NtNotifyChangeKey Status = 0x%lx\n",NtStatus));
+ return(status);
+
+} // InitNotify
+
+
+BOOL
+ElfSetupMonitor(
+ LPREG_MONITOR_INFO pMonitorInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This function submits a request for a registry NotifyChangeKey
+ and then submits a work item to the service controller thread
+ management system to wait for the Notification handle to become
+ signaled.
+
+Arguments:
+
+ pMonitorInfo - This is a pointer to a MONITOR_INFO structure. This
+ function fills in the WorkItemHandle member of that structure
+ if successfully adds a new work item.
+
+Return Value:
+
+ TRUE - if successful in setting up.
+ FALSE - if unsuccessful. A work item hasn't been submitted, and
+ we won't be listening for registry changes.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Call NtNotifyChange Key indirectly via the service controller
+ // Watcher thread. This way the thread that created the I/O
+ // request will always be around.
+ //
+ pMonitorInfo->WorkItemHandle = ElfGlobalData->SvcsAddWorkItem(
+ NULL,
+ InitNotify,
+ (PVOID)pMonitorInfo,
+ SVC_IMMEDIATE_CALLBACK,
+ INFINITE,
+ NULL);
+
+ if (pMonitorInfo->WorkItemHandle == NULL) {
+ ElfDbgPrint(("[ELF]Couldn't Initialize Registry Notify %d\n",GetLastError()));
+ return(FALSE);
+ }
+ //
+ // Add the work item that is to be called when the
+ // NotifyEventHandle is signalled.
+ //
+ pMonitorInfo->WorkItemHandle = ElfGlobalData->SvcsAddWorkItem(
+ pMonitorInfo->NotifyEventHandle,
+ ElfRegistryMonitor,
+ (PVOID)pMonitorInfo,
+ SVC_QUEUE_WORK_ITEM,
+ pMonitorInfo->Timeout,
+ ElfGlobalSvcRefHandle);
+
+ if (pMonitorInfo->WorkItemHandle == NULL) {
+ ElfDbgPrint(("[ELF]Couldn't add Reg Monitor work item\n"));
+ return(FALSE);
+ }
+ return(TRUE);
+
+} // ElfSetupMonitor
+
+
+BOOL
+ElfStartRegistryMonitor()
+
+/*++
+
+Routine Description:
+
+ This routine starts up the thread that monitors changes in the registry.
+
+ This function calls ElfSetupMonitor() to register for the change
+ notification and to submit a work item to wait for the registry
+ change event to get signaled. When signalled, the ElfRegistryMonitor()
+ callback function is called by a thread from the services thread pool.
+ This callback function services the notification.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ TRUE if thread creation succeeded, FALSE otherwise.
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status;
+ HANDLE hNotifyEvent=NULL;
+
+ ElfDbgPrint(("[ELF] Starting registry monitor\n"));
+
+ if (hEventLogNode == NULL) {
+ ElfDbgPrint(("[ELF]No EventLog node in registry - Exit Monitor"));
+ return(FALSE);
+ }
+
+ //
+ // Create an event to wait on
+ //
+
+ Status = NtCreateEvent (&hNotifyEvent, EVENT_ALL_ACCESS,
+ NULL, NotificationEvent, FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF]Couldn't create event for registry monitor"));
+ return(FALSE);
+ }
+
+ //
+ // Fill in the Monitor info structure with the event handle
+ // and a 5 minute timeout.
+ //
+
+ GlRegMonitorInfo.NotifyEventHandle = hNotifyEvent;
+ GlRegMonitorInfo.Timeout = 5 * 60 * 1000;
+ GlRegMonitorInfo.WorkItemHandle = NULL;
+
+ //
+ // Setup for the change notify and
+ // submit the work item to the service controller.
+ //
+
+ if (!ElfSetupMonitor(&GlRegMonitorInfo)) {
+ return(FALSE);
+ }
+ return(TRUE);
+
+} // ElfStartRegistryMonitor
+
+
+VOID
+StopRegistryMonitor ()
+
+/*++
+
+Routine Description:
+
+ This routine wakes up the work item that has been submitted for the
+ purpose of monitoring registry eventlog changes. The thread created
+ to service that work item will actually do the clean-up of the monitor
+ thread.
+
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+--*/
+
+{
+ ElfDbgPrint (("[ELF] Stopping registry monitor\n"));
+
+ //
+ // Wake up the RegistryMonitorThread.
+ //
+ if (GlRegMonitorInfo.NotifyEventHandle != NULL ) {
+ SetEvent(GlRegMonitorInfo.NotifyEventHandle);
+ }
+
+ return;
+
+} // StopRegistryMonitor
+
+
+NTSTATUS
+ReadRegistryInfo (
+ HANDLE hLogFile,
+ PUNICODE_STRING SubKeyName,
+ PLOG_FILE_INFO LogFileInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reads in the information from the node pointed to by
+ hLogFile and stores it in the a structure so that the
+ necessary data structures can be set up for the service.
+
+ ALLOCATIONS: If successful, this function allocates memory for
+ LogFileInfo->LogFileName. It is the responsiblilty of the caller
+ to free this memory.
+
+Arguments:
+
+ hLogFile - A handle to the Eventlog\<somelogfile> node in the registry
+ KeyName - The subkey for this logfile to open
+ LogFileInfo - The structure to fill in with the data
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+
+#define EXPAND_BUFFER_SIZE 64
+
+ NTSTATUS Status;
+ BOOLEAN RegistryCorrupt = FALSE;
+ BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
+ ULONG ActualSize;
+ UNICODE_STRING ValueName;
+ UNICODE_STRING UnexpandedName;
+ UNICODE_STRING ExpandedName;
+ ULONG NumberOfBytes = 0;
+ BYTE ExpandNameBuffer[EXPAND_BUFFER_SIZE];
+ PUNICODE_STRING FileNameString;
+ LPWSTR FileName;
+ BOOL ExpandedBufferWasAllocated=FALSE;
+ PKEY_VALUE_FULL_INFORMATION ValueBuffer =
+ (PKEY_VALUE_FULL_INFORMATION) Buffer;
+
+ ASSERT(hLogFile);
+
+ // MaxSize
+
+ RtlInitUnicodeString(&ValueName, VALUE_MAXSIZE);
+
+ Status = NtQueryValueKey(hLogFile, &ValueName,
+ KeyValueFullInformation, ValueBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] - Logfile %ws Maxsize doesn't exist\n",
+ SubKeyName->Buffer));
+ LogFileInfo->MaxFileSize = ELF_DEFAULT_MAX_FILE_SIZE;
+ RegistryCorrupt = TRUE;
+ }
+ else {
+ LogFileInfo->MaxFileSize = *((PULONG)(Buffer +
+ ValueBuffer->DataOffset));
+ }
+
+
+ // Retention period
+
+ RtlInitUnicodeString(&ValueName, VALUE_RETENTION);
+
+ Status = NtQueryValueKey(hLogFile, &ValueName,
+ KeyValueFullInformation, ValueBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] - Logfile %ws Retention doesn't exist\n",
+ SubKeyName->Buffer));
+ LogFileInfo->Retention = ELF_DEFAULT_RETENTION_PERIOD;
+ RegistryCorrupt = TRUE;
+ }
+ else {
+ LogFileInfo->Retention = *((PULONG)(Buffer +
+ ValueBuffer->DataOffset));
+ }
+
+
+ // RestrictGuestAccess
+
+ RtlInitUnicodeString(&ValueName, VALUE_RESTRICT_GUEST_ACCESS);
+
+ Status = NtQueryValueKey(hLogFile, &ValueName,
+ KeyValueFullInformation, ValueBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+ if (!NT_SUCCESS(Status)) {
+ LogFileInfo->GuestAccessRestriction = ELF_GUEST_ACCESS_UNRESTRICTED;
+ }
+ else {
+ if (*((PULONG)(Buffer + ValueBuffer->DataOffset)) == 1) {
+ LogFileInfo->GuestAccessRestriction = ELF_GUEST_ACCESS_RESTRICTED;
+ }
+ else {
+ LogFileInfo->GuestAccessRestriction =
+ ELF_GUEST_ACCESS_UNRESTRICTED;
+ }
+ }
+
+
+ // Filename
+
+ RtlInitUnicodeString(&ValueName, VALUE_FILENAME);
+
+ Status = NtQueryValueKey(hLogFile, &ValueName,
+ KeyValueFullInformation, ValueBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // Allocate the buffer for the UNICODE_STRING for the filename and
+ // initialize it. (41 = \Systemroot\system32\config\xxxxxxxx.evt)
+ //
+
+ FileNameString = ElfpAllocateBuffer(41 * sizeof(WCHAR) +
+ sizeof(UNICODE_STRING));
+ if (!FileNameString) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ LogFileInfo->LogFileName = FileNameString;
+ FileName = (LPWSTR)(FileNameString + 1);
+ wcscpy(FileName, L"\\Systemroot\\System32\\Config\\");
+ wcsncat(FileName, SubKeyName->Buffer, 8);
+ wcscat(FileName, L".evt");
+ RtlInitUnicodeString(FileNameString, FileName);
+
+ RegistryCorrupt = TRUE;
+
+ }
+ else {
+
+ //
+ // If it's a REG_EXPAND_SZ expand it
+ //
+
+ if (ValueBuffer->Type == REG_EXPAND_SZ) {
+
+ //
+ // Initialize the UNICODE_STRING, when the string isn't null
+ // terminated
+ //
+
+ UnexpandedName.MaximumLength = UnexpandedName.Length =
+ (USHORT) ValueBuffer->DataLength;
+ UnexpandedName.Buffer = (PWSTR) ((PBYTE) ValueBuffer +
+ ValueBuffer->DataOffset);
+
+ //
+ // Call the magic expand-o api
+ //
+
+ ExpandedName.Length = ExpandedName.MaximumLength =
+ EXPAND_BUFFER_SIZE;
+ ExpandedName.Buffer = (LPWSTR) ExpandNameBuffer;
+ Status = RtlExpandEnvironmentStrings_U(NULL, &UnexpandedName,
+ &ExpandedName, &NumberOfBytes);
+
+ if (NumberOfBytes > EXPAND_BUFFER_SIZE) {
+
+ //
+ // The default buffer wasn't big enough. Allocate a
+ // bigger one and try again
+ //
+
+ ExpandedName.Length = ExpandedName.MaximumLength =
+ (USHORT) NumberOfBytes;
+ ExpandedName.Buffer = ElfpAllocateBuffer(ExpandedName.Length);
+ if (!ExpandedName.Buffer) {
+ return(STATUS_NO_MEMORY);
+ }
+ ExpandedBufferWasAllocated = TRUE;
+
+ Status = RtlExpandEnvironmentStrings_U(NULL, &UnexpandedName,
+ &ExpandedName, &NumberOfBytes);
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ if (ExpandedBufferWasAllocated) {
+ ElfpFreeBuffer(ExpandedName.Buffer);
+ }
+ return(Status);
+ }
+ }
+ else {
+
+ //
+ // It doesn't need to be expanded, just set up the UNICODE_STRING
+ // for the conversion to an NT pathname
+ //
+
+ ExpandedName.MaximumLength = ExpandedName.Length =
+ (USHORT) ValueBuffer->DataLength;
+ ExpandedName.Buffer = (PWSTR) ((PBYTE) ValueBuffer +
+ ValueBuffer->DataOffset);
+ }
+
+ //
+ // Now convert from a DOS pathname to an NT pathname
+ //
+ // Need to allocate this since it needs to stay around
+ //
+
+ //
+ // Translate to NtPathName.
+ // NOTE: this allocates a buffer for ValueName.Buffer.
+ //
+ if (!RtlDosPathNameToNtPathName_U(ExpandedName.Buffer,
+ &ValueName, NULL, NULL)) {
+
+ if (ExpandedBufferWasAllocated) {
+ ElfpFreeBuffer(ExpandedName.Buffer);
+ }
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ //
+ // Allocate memory for the unicode string structure and the buffer
+ // so that it can be free'd with a single call.
+ //
+ FileNameString = ElfpAllocateBuffer(
+ sizeof(UNICODE_STRING) +
+ ((ValueName.Length + 1) * sizeof(WCHAR)));
+
+ if (!FileNameString) {
+ if (ExpandedBufferWasAllocated) {
+ ElfpFreeBuffer(ExpandedName.Buffer);
+ }
+ RtlFreeHeap(RtlProcessHeap(),0,ValueName.Buffer);
+ return(STATUS_NO_MEMORY);
+ }
+
+ //
+ // Copy the NtPathName string into the new buffer, and initialize
+ // the unicode string.
+ //
+ FileName = (LPWSTR)(FileNameString + 1);
+ wcsncpy(FileName, ValueName.Buffer, ValueName.Length);
+ *(FileName+ValueName.Length) = L'\0';
+ RtlInitUnicodeString(FileNameString, FileName);
+
+ //
+ // Free memory allocated by RtlDosPathNAmeToNtPathName.
+ //
+ RtlFreeHeap(RtlProcessHeap(),0,ValueName.Buffer);
+
+ //
+ // Clean up if I had to allocate a bigger buffer than the default
+ //
+
+ if (ExpandedBufferWasAllocated) {
+ ElfpFreeBuffer(ExpandedName.Buffer);
+ }
+
+ }
+
+ //
+ // Add the LogFileName to the LogFileInfo structure.
+ //
+ LogFileInfo->LogFileName = FileNameString;
+
+ //
+ // If we didn't find all the required values, tell someone
+ //
+
+ if (RegistryCorrupt) {
+ ElfDbgPrintNC(("[ELF] Registry information for %ws invalid\n",
+ SubKeyName->Buffer));
+ }
+
+ return(STATUS_SUCCESS);
+
+}
+
diff --git a/private/eventlog/server/control.c b/private/eventlog/server/control.c
new file mode 100644
index 000000000..6f937f510
--- /dev/null
+++ b/private/eventlog/server/control.c
@@ -0,0 +1,699 @@
+
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ CONTROL.C
+
+Abstract:
+
+ This file contains the control handler for the eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 16-Jul-1991
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+
+
+//
+// GLOBALS
+//
+
+ CRITICAL_SECTION StatusCriticalSection={0};
+ SERVICE_STATUS ElStatus={0};
+ DWORD HintCount=0;
+ DWORD ElUninstallCode=0; // reason for uninstalling
+ DWORD ElSpecificCode=0;
+ DWORD ElState=STARTING;
+
+
+VOID
+ElfPrepareForPause()
+
+/*++
+
+Routine Description:
+
+ This routine prepares the eventlog service for pausing. Since the
+ caller took the global resource for exclusive access, we know that
+ there is no activity on any of the files.
+
+ We just have to note somewhere that the service is paused
+ so that any threads that start running will know that and will not
+ perform any further operations until the service is CONTINUEd.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ ElfDbgPrint(("[ELF] Control: Prepare for service pause\n"));
+
+ //
+ // Flush all files to disk.
+ //
+ Status = ElfpFlushFiles ();
+
+ return;
+}
+
+
+
+VOID
+ElfPrepareForContinue()
+
+/*++
+
+Routine Description:
+
+ This routine restarts the eventlog service after a pause operation.
+ It will signal the relevant event(s) for all the threads to start
+ running.
+
+ The caller ensures that it has exclusive access to the global resource
+ so that there is no thread inside the service that is doing operations
+ on the log file(s) or the data structures while the control request
+ is being handled.
+
+Arguments:
+
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ ElfDbgPrint(("[ELF] Control: Prepare for service continue\n"));
+
+ //
+ // Signal the "continue" event.
+ //
+
+
+ return;
+}
+
+
+
+VOID
+ElfControlResponse(
+ DWORD opCode
+ )
+
+{
+ DWORD state;
+
+ ElfDbgPrint(("[ELF] Inside control handler. Control = %ld\n", opCode));
+
+ //
+ // Determine the type of service control message and modify the
+ // service status, if necessary.
+ //
+ switch(opCode) {
+
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+
+
+ //
+ // If the service is installed, shut it down and exit.
+ //
+
+ ElfStatusUpdate(STOPPING);
+
+ GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
+
+ //
+ // Log an event that says we're stopping
+ //
+
+#if DBG
+ ElfpCreateElfEvent(EVENT_EventlogStopped,
+ EVENTLOG_INFORMATION_TYPE,
+ 0, // EventCategory
+ 0, // NumberOfStrings
+ NULL, // Strings
+ NULL, // Data
+ 0, // Datalength
+ 0 // flags
+ );
+
+ //
+ // Now force it to be written before we shut down
+ //
+
+ WriteQueuedEvents();
+#endif
+ ReleaseGlobalResource();
+ //
+ // If the RegistryMonitor is started, wakeup that
+ // worker thread and have it handle the rest of the
+ // shutdown.
+ //
+ // Otherwise The main thread should pick up the
+ // fact that a shutdown during startup is occuring.
+ //
+ if (EventFlags & ELF_STARTED_REGISTRY_MONITOR) {
+ StopRegistryMonitor();
+ }
+
+ break ;
+
+ case SERVICE_CONTROL_PAUSE:
+
+ //
+ // If the service is not already paused, pause it.
+ //
+ state = GetElState();
+ if ((state != PAUSED) && (state != PAUSING)) {
+
+ GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
+
+ // NOTE: If there was any service-related pause cleanup, we'd
+ // set the status to PAUSE_PENDING, announce it, and
+ // then do the cleanup.
+ //
+
+ // Announce that the service is about to be paused
+ ElfStatusUpdate(PAUSING);
+
+ // Get into a decent state to pause the service.
+
+ ElfPrepareForPause();
+
+
+ // Set the status and announce that the service is paused
+ ElfStatusUpdate(PAUSED);
+
+ ReleaseGlobalResource();
+ }
+
+ break ;
+
+ case SERVICE_CONTROL_CONTINUE:
+
+ //
+ // If the service is not already running, un-pause it.
+ //
+
+ state = GetElState();
+ if ((state != RUNNING) && (state != CONTINUING)) {
+
+ GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
+
+ // NOTE: If there was any service-related continue cleanup, we'd
+ // set the status to CONTINUE_PENDING, announce it, and
+ // then do the cleanup.
+ //
+
+ // Announce that the service is about to be continued
+
+ ElfStatusUpdate(CONTINUING);
+
+ // Start up the service.
+
+ ElfPrepareForContinue();
+
+
+ // Set the status and announce that the service is no longer
+ // paused
+ //
+ ElfStatusUpdate(RUNNING);
+
+ ReleaseGlobalResource();
+ }
+
+ break ;
+
+ case SERVICE_CONTROL_INTERROGATE:
+
+ // Do nothing; the status gets announced below
+
+ default:
+ // WARNING: This should never happen.
+ ElfStatusUpdate(UPDATE_ONLY);
+ break ;
+ }
+
+ return ;
+
+}
+
+DWORD
+ElfBeginForcedShutdown(
+ IN BOOL PendingCode,
+ IN DWORD ExitCode,
+ IN DWORD ServiceSpecificCode
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ DWORD status;
+
+ EnterCriticalSection(&StatusCriticalSection);
+
+ ElfDbgPrint(("BeginForcedShutdown: Errors - %d 0x%lx\n",
+ ExitCode, ServiceSpecificCode));
+ //
+ // See if the eventlog is already stopping for some reason.
+ // It could be that the ControlHandler thread received a control to
+ // stop the eventlog just as we decided to stop ourselves.
+ //
+ if ((ElState != STOPPING) || (ElState != STOPPED)) {
+ if (PendingCode == PENDING) {
+ ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ ElState = STOPPING;
+ }
+ else {
+ //
+ // The shutdown is to take immediate effect.
+ //
+ ElStatus.dwCurrentState = SERVICE_STOPPED;
+ ElStatus.dwControlsAccepted = 0;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+ ElState = STOPPED;
+ }
+
+ ElUninstallCode = ExitCode;
+ ElSpecificCode = ServiceSpecificCode;
+
+ ElStatus.dwWin32ExitCode = ExitCode;
+ ElStatus.dwServiceSpecificExitCode = ServiceSpecificCode;
+ }
+
+ //
+ // Send the new status to the service controller.
+ //
+ if (ElfServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ ElfDbgPrint(("ElfBeginForcedShutdown, no handle to call SetServiceStatus\n"));
+
+ }
+ else if (! SetServiceStatus( ElfServiceStatusHandle, &ElStatus )) {
+
+ status = GetLastError();
+
+ if (status != NERR_Success) {
+ ElfDbgPrint(("ElfBeginForcedShutdown,SetServiceStatus Failed %X\n",
+ status));
+ }
+ }
+
+ status = ElState;
+ LeaveCriticalSection(&StatusCriticalSection);
+ return(status);
+}
+
+
+DWORD
+ElfStatusUpdate(
+ IN DWORD NewState
+ )
+
+/*++
+
+Routine Description:
+
+ Sends a status to the Service Controller via SetServiceStatus.
+
+ The contents of the status message is controlled by this routine.
+ The caller simply passes in the desired state, and this routine does
+ the rest. For instance, if the Eventlog passes in a STARTING state,
+ This routine will update the hint count that it maintains, and send
+ the appropriate information in the SetServiceStatus call.
+
+ This routine uses transitions in state to send determine which status
+ to send. For instance if the status was STARTING, and has changed
+ to RUNNING, this routine sends out an INSTALLED to the Service
+ Controller.
+
+Arguments:
+
+ NewState - Can be any of the state flags:
+ UPDATE_ONLY - Simply send out the current status
+ STARTING - The Eventlog is in the process of initializing
+ RUNNING - The Eventlog has finished with initialization
+ STOPPING - The Eventlog is in the process of shutting down
+ STOPPED - The Eventlog has completed the shutdown.
+ PAUSING -
+ CONTINUING -
+ PAUSED -
+
+
+Return Value:
+
+ CurrentState - This may not be the same as the NewState that was
+ passed in. It could be that the main thread is sending in a new
+ install state just after the Control Handler set the state to
+ STOPPING. In this case, the STOPPING state will be returned so as
+ to inform the main thread that a shut-down is in process.
+
+Note:
+
+
+--*/
+
+{
+ DWORD status;
+ BOOL inhibit = FALSE; // Used to inhibit sending the status
+ // to the service controller.
+
+ EnterCriticalSection(&StatusCriticalSection);
+
+ ElfDbgPrint(("ElfStatusUpdate (entry) NewState = %d, OldState = %d\n",
+ NewState,ElState));
+
+ if (NewState == STOPPED) {
+ if (ElState == STOPPED) {
+ //
+ // It was already stopped, don't send another SetServiceStatus.
+ //
+ inhibit = TRUE;
+ }
+ else {
+ //
+ // The shut down is complete, indicate that the eventlog
+ // has stopped.
+ //
+ ElStatus.dwCurrentState = SERVICE_STOPPED;
+ ElStatus.dwControlsAccepted = 0;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+
+ ElStatus.dwWin32ExitCode = ElUninstallCode;
+ ElStatus.dwServiceSpecificExitCode = ElSpecificCode;
+
+ }
+ ElState = NewState;
+ }
+ else if (NewState == UPDATE_ONLY) {
+ inhibit = FALSE;
+ }
+ else {
+ //
+ // We are not being asked to change to the STOPPED state.
+ //
+ switch(ElState) {
+
+ case STARTING:
+ if (NewState == STOPPING) {
+
+ ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ ElStatus.dwControlsAccepted = 0;
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ ElState = NewState;
+
+ EventlogShutdown = TRUE;
+ }
+
+ else if (NewState == RUNNING) {
+
+ //
+ // The Eventlog Service has completed installation.
+ //
+ ElStatus.dwCurrentState = SERVICE_RUNNING;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+ //
+ // Only stoppable/pausable if developer's build.
+ //
+#if DBG
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+#else
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
+#endif
+ ElState = NewState;
+ }
+
+ else {
+ //
+ // The NewState must be STARTING. So update the pending
+ // count
+ //
+
+ ElStatus.dwCurrentState = SERVICE_START_PENDING;
+ ElStatus.dwControlsAccepted = 0;
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ }
+ break;
+
+ case RUNNING:
+ if (NewState == STOPPING) {
+
+ ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ ElStatus.dwControlsAccepted = 0;
+
+ EventlogShutdown = TRUE;
+ }
+ else if (NewState == PAUSING) {
+ ElStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
+ }
+ else if (NewState == PAUSED) {
+ ElStatus.dwCurrentState = SERVICE_PAUSED;
+ }
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ ElState = NewState;
+
+ break;
+
+ case STOPPING:
+ //
+ // No matter what else was passed in, force the status to
+ // indicate that a shutdown is pending.
+ //
+ ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ ElStatus.dwControlsAccepted = 0;
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ EventlogShutdown = TRUE;
+
+ break;
+
+ case STOPPED:
+ //
+ // We're already stopped. Therefore, an uninstalled status
+ // has already been sent. Do nothing.
+ //
+ inhibit = TRUE;
+ break;
+ case PAUSING:
+ if (NewState == PAUSED) {
+ ElStatus.dwCurrentState = SERVICE_PAUSED;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+ }
+ else {
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ }
+ ElState = NewState;
+ break;
+ case CONTINUING:
+ if (NewState == RUNNING) {
+ //
+ // The Eventlog Service has completed installation.
+ //
+ ElStatus.dwCurrentState = SERVICE_RUNNING;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+ //
+ // Only stoppable/pausable if developer's build.
+ //
+#if DBG
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+#else
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
+#endif
+ ElState = NewState;
+ }
+ else {
+ ElStatus.dwCheckPoint = HintCount++;
+ }
+ break;
+ case PAUSED:
+ if (NewState == CONTINUING) {
+ ElStatus.dwCheckPoint = HintCount++;
+ ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
+ ElStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
+ }
+ else if (NewState == RUNNING) {
+ //
+ // The Eventlog Service has completed installation.
+ //
+ ElStatus.dwCurrentState = SERVICE_RUNNING;
+ ElStatus.dwCheckPoint = 0;
+ ElStatus.dwWaitHint = 0;
+ //
+ // Only stoppable/pausable if developer's build.
+ //
+#if DBG
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+#else
+ ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
+#endif
+ ElState = NewState;
+ }
+ break;
+ }
+ }
+
+ if (!inhibit) {
+ if (ElfServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ ElfDbgPrint(("ElfStatusUpdate, no handle to call SetServiceStatus\n"));
+
+ }
+ else if (! SetServiceStatus( ElfServiceStatusHandle, &ElStatus )) {
+
+ status = GetLastError();
+
+ if (status != NERR_Success) {
+ ElfDbgPrint(("ElfStatusUpdate, SetServiceStatus Failed %d\n",
+ status));
+ }
+ }
+ }
+
+ ElfDbgPrint(("ElfStatusUpdate (exit) State = %d\n",ElState));
+ status = ElState;
+ LeaveCriticalSection(&StatusCriticalSection);
+ return(status);
+}
+
+
+DWORD
+GetElState (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Obtains the state of the Eventlog Service. This state information
+ is protected as a critical section such that only one thread can
+ modify or read it at a time.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ The Eventlog State is returned as the return value.
+
+--*/
+{
+ DWORD status;
+
+ EnterCriticalSection(&StatusCriticalSection);
+ status = ElState;
+ LeaveCriticalSection(&StatusCriticalSection);
+
+ return(status);
+}
+
+
+VOID
+ElInitStatus(VOID)
+
+/*++
+
+Routine Description:
+
+ Initializes the critical section that is used to guard access to the
+ status database.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+Note:
+
+
+--*/
+{
+ InitializeCriticalSection(&StatusCriticalSection);
+
+ ElStatus.dwCurrentState = SERVICE_START_PENDING;
+ ElStatus.dwServiceType = SERVICE_WIN32;
+
+}
+
+
+VOID
+ElCleanupStatus(VOID)
+
+/*++
+
+Routine Description:
+
+ Deletes the critical section used to control access to the thread and
+ status database.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+Note:
+
+
+--*/
+{
+ DeleteCriticalSection(&StatusCriticalSection);
+}
+
+
diff --git a/private/eventlog/server/copy.c b/private/eventlog/server/copy.c
new file mode 100644
index 000000000..3344bc3e4
--- /dev/null
+++ b/private/eventlog/server/copy.c
@@ -0,0 +1,196 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ copy.c
+
+Abstract:
+
+ This module contains the routine to copy a file.
+
+Author:
+
+ Dan Hinsley (DanHi) 24-Feb-1991
+
+Revision History:
+
+ 02-Feb-1994 Danl
+ Fixed memory leak where ioBuffer wasn't getting free'd when doing
+ an error exit from ElfpCopyFile.
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+
+
+NTSTATUS
+ElfpCopyFile (
+ IN HANDLE SourceHandle,
+ IN PUNICODE_STRING TargetFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies or appends from the source file to the target file.
+ If the target file already exists, the copy fails.
+
+Arguments:
+
+ SourceHandle - An open handle to the source file.
+
+ TargetFileName - The name of the file to copy to.
+
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS or error.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ FILE_STANDARD_INFORMATION sourceStandardInfo;
+
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE TargetHandle;
+
+ PCHAR ioBuffer;
+ ULONG ioBufferSize;
+ ULONG bytesRead;
+
+ //
+ // Get the size of the file so we can set the attributes of the target
+ // file.
+ //
+
+ Status = NtQueryInformationFile(
+ SourceHandle,
+ &IoStatusBlock,
+ &sourceStandardInfo,
+ sizeof(sourceStandardInfo),
+ FileStandardInformation
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ return(Status);
+ }
+
+ //
+ // Open the target file, fail if the file already exists.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ TargetFileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtCreateFile(&TargetHandle,
+ GENERIC_WRITE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ &(sourceStandardInfo.EndOfFile),
+ FILE_ATTRIBUTE_NORMAL,
+ 0, // Share access
+ FILE_CREATE,
+ FILE_SYNCHRONOUS_IO_ALERT | FILE_SEQUENTIAL_ONLY,
+ NULL, // EA buffer
+ 0 // EA length
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ //
+ // Allocate a buffer to use for the data copy.
+ //
+
+ ioBufferSize = 4096;
+
+ ioBuffer = ElfpAllocateBuffer ( ioBufferSize);
+
+ if ( ioBuffer == NULL ) {
+
+ return (STATUS_NO_MEMORY);
+ }
+
+ //
+ // Copy data--read from source, write to target. Do this until
+ // all the data is written or an error occurs.
+ //
+
+ while ( TRUE ) {
+
+ Status = NtReadFile(
+ SourceHandle,
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &IoStatusBlock,
+ ioBuffer,
+ ioBufferSize,
+ NULL, // ByteOffset
+ NULL // Key
+ );
+
+ if ( !NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE ) {
+
+ ElfDbgPrint(("[ELF] Copy failed reading source file - %X\n",
+ Status));
+ ElfpFreeBuffer(ioBuffer);
+ return (Status);
+
+ }
+
+ if ( IoStatusBlock.Information == 0 ||
+ Status == STATUS_END_OF_FILE ) {
+ break;
+ }
+
+ bytesRead = IoStatusBlock.Information;
+
+ Status = NtWriteFile(
+ TargetHandle,
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &IoStatusBlock,
+ ioBuffer,
+ bytesRead,
+ NULL, // ByteOffset
+ NULL // Key
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrint(("[ELF] Copy failed writing target file - %X\n",
+ Status));
+ ElfpFreeBuffer(ioBuffer);
+ return (Status);
+ }
+ }
+
+ ElfpFreeBuffer ( ioBuffer );
+
+ Status = NtClose(TargetHandle);
+
+ ASSERT(NT_SUCCESS(Status));
+
+ return STATUS_SUCCESS;
+
+} // ElfpCopyFile
+
diff --git a/private/eventlog/server/daytona/makefile b/private/eventlog/server/daytona/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/eventlog/server/daytona/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/eventlog/server/daytona/sources b/private/eventlog/server/daytona/sources
new file mode 100644
index 000000000..c27140453
--- /dev/null
+++ b/private/eventlog/server/daytona/sources
@@ -0,0 +1,69 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Rajen Shah (rajens) 2-Jul-1991
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = eventlog
+MINORCOMP = server
+
+TARGETPATH=obj
+TARGETNAME= eventlog
+TARGETTYPE=DYNLINK
+
+DLLDEF=..\eventlog.def
+
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib
+
+INCLUDES=..\;..\..;$(BASEDIR)\public\sdk\inc;$(BASEDIR)\private\inc
+
+SOURCES= \
+ ..\eventlog.rc \
+ ..\eventlog.c \
+ ..\alert.c \
+ ..\config.c \
+ ..\control.c \
+ ..\copy.c \
+ ..\elfapi.c \
+ ..\elfdata.c \
+ ..\elflpc.c \
+ ..\elfrpc.c \
+ ..\elfsec.c \
+ ..\elfutil.c \
+ ..\elf_s.c \
+ ..\file.c \
+ ..\memory.c \
+ ..\operate.c \
+ ..\logclear.c \
+ ..\terminat.c
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H
+
+USE_CRTDLL=1
+
+UMTYPE=windows
diff --git a/private/eventlog/server/dirs b/private/eventlog/server/dirs
new file mode 100644
index 000000000..945735ae2
--- /dev/null
+++ b/private/eventlog/server/dirs
@@ -0,0 +1,25 @@
+!IF 0
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles. This was necessitated by the need to
+ build a and nt rdr.
+
+Author:
+
+ Milan Shah (April 20, 1994)
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=daytona
+
+OPTIONAL_DIRS=
diff --git a/private/eventlog/server/elfapi.c b/private/eventlog/server/elfapi.c
new file mode 100644
index 000000000..5ffa2a2fd
--- /dev/null
+++ b/private/eventlog/server/elfapi.c
@@ -0,0 +1,2235 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ELFAPI.C
+
+Abstract:
+
+ This module contains the server end of the Elf APIs.
+
+
+Author:
+
+ Rajen Shah (rajens) 29-Jul-1991
+
+
+Revision History:
+ 30-Jan-1995 MarkBl
+ Backup operators are allowed to _open the security log, but are only
+ allowed to perform backup operations on it; all other operations are
+ disallowed.
+
+ 13-Oct-1993 Danl
+ ElfrOpenELA: Fixed Memory Leak bug where it was not calling
+ RtlFreeUnicodeString for pRegModuleNameU and PModuleNameU.
+
+ 29-Jul-1991 RajenS
+ Created
+
+--*/
+
+
+//#include <rpcutil.h>
+
+#include <eventp.h>
+#include <elfcfg.h>
+#include <stdio.h> // sprintf
+#include <stdlib.h>
+#include <memory.h>
+
+//
+// PROTOTYPES
+//
+NTSTATUS
+ElfpOpenELW (
+ IN EVENTLOG_HANDLE_W UNCServerName,
+ IN PRPC_UNICODE_STRING ModuleName,
+ IN PRPC_UNICODE_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle,
+ IN ULONG DesiredAccess
+ );
+
+NTSTATUS
+ElfpOpenELA (
+ IN EVENTLOG_HANDLE_A UNCServerName,
+ IN PRPC_STRING ModuleName,
+ IN PRPC_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle,
+ IN ULONG DesiredAccess
+ );
+
+VOID
+FreePUStringArray (
+ IN PUNICODE_STRING * pUStringArray,
+ IN USHORT NumStrings
+ );
+
+
+//
+// These APIs only have one interface, since they don't take or return strings
+//
+
+NTSTATUS
+ElfrNumberOfRecords(
+ IN IELF_HANDLE LogHandle,
+ OUT PULONG NumberOfRecords
+ )
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrCurrentRecord API.
+
+Arguments:
+
+ LogHandle - The context-handle for this module's call.
+
+ NumberOfRecords - Where to return the total number of records in the
+ log file.
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+
+ PLOGMODULE Module;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // If the OldestRecordNumber is 0, that means we have an empty
+ // file, else we calculate the difference between the oldest
+ // and next record numbers
+ //
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+ if (Module != NULL) {
+ *NumberOfRecords = Module->LogFile->OldestRecordNumber == 0 ? 0 :
+ Module->LogFile->CurrentRecordNumber -
+ Module->LogFile->OldestRecordNumber;
+ }
+ else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+ return (Status);
+
+}
+
+
+NTSTATUS
+ElfrOldestRecord(
+ IN IELF_HANDLE LogHandle,
+ OUT PULONG OldestRecordNumber
+ )
+{
+ PLOGMODULE Module;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+ if (Module != NULL) {
+ *OldestRecordNumber = Module->LogFile->OldestRecordNumber;
+ }
+ else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+ return (Status);
+}
+
+NTSTATUS
+ElfrChangeNotify(
+ IN IELF_HANDLE LogHandle,
+ IN RPC_CLIENT_ID ClientId,
+ IN ULONG Event
+ )
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE ProcessHandle = NULL;
+ HANDLE EventHandle;
+ PLOGMODULE Module;
+ PNOTIFIEE Notifiee;
+
+ //
+ // First make sure that this is a local call and that it is not a
+ // handle that was created for a backup log file
+ //
+
+ if (LogHandle->Flags & ELF_LOG_HANDLE_REMOTE_HANDLE ||
+ LogHandle->Flags & ELF_LOG_HANDLE_BACKUP_LOG) {
+ return(STATUS_INVALID_HANDLE);
+ }
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // First get a handle to the process using the passed in ClientId
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ NULL, // UNICODE string
+ 0, // Attributes
+ NULL, // Root directory
+ NULL); // Security descriptor
+
+ Status = NtOpenProcess(
+ &ProcessHandle,
+ PROCESS_DUP_HANDLE,
+ &ObjectAttributes,
+ (PCLIENT_ID) &ClientId);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Now dupe the handle they passed in for the event
+ //
+
+ Status = NtDuplicateObject(
+ ProcessHandle,
+ (HANDLE) Event,
+ NtCurrentProcess(),
+ &EventHandle,
+ 0,
+ 0,
+ DUPLICATE_SAME_ACCESS);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Create a new NOTIFIEE control block to link in
+ //
+
+ Notifiee = ElfpAllocateBuffer(sizeof(NOTIFIEE));
+ if (Notifiee) {
+
+ //
+ // Fill in the fields
+ //
+
+ Notifiee->Handle = LogHandle;
+ Notifiee->Event = EventHandle;
+
+ //
+ // Find the LOGFILE associated with this handle
+ //
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+ ASSERT(Module);
+
+ //
+ // Get exclusive access to the log file. This will ensure
+ // no one else is accessing the file.
+ //
+
+ RtlAcquireResourceExclusive (
+ &Module->LogFile->Resource,
+ TRUE // Wait until available
+ );
+
+ InsertHeadList(&Module->LogFile->Notifiees,
+ &Notifiee->Next);
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &Module->LogFile->Resource );
+ }
+ else {
+ Status = STATUS_NO_MEMORY;
+ }
+ }
+ }
+ else {
+ if (Status == STATUS_INVALID_CID) {
+ Status = STATUS_INVALID_HANDLE;
+ }
+ }
+
+ if (ProcessHandle) {
+ NtClose(ProcessHandle);
+ }
+
+ return (Status);
+}
+
+
+//
+// UNICODE APIs
+//
+
+
+
+NTSTATUS
+ElfrClearELFW (
+ IN IELF_HANDLE LogHandle,
+ IN PRPC_UNICODE_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrClearELFW API.
+
+Arguments:
+
+ LogHandle - The context-handle for this module's call. This must
+ not have been returned from OpenBackupEventlog, or
+ this call will fail with invalid handle.
+
+ BackupFileName - Name of the file to back up the current log file.
+ NULL implies not to back up the file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status=STATUS_SUCCESS;
+ PLOGMODULE Module;
+ ELF_REQUEST_RECORD Request;
+ CLEAR_PKT ClearPkt;
+ DWORD status=NO_ERROR;
+
+ //
+ // Can't clear a backup log
+ //
+
+ if (LogHandle->Flags & ELF_LOG_HANDLE_BACKUP_LOG) {
+ return(STATUS_INVALID_HANDLE);
+ }
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // Find the matching module structure
+ //
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+
+ Request.Pkt.ClearPkt = &ClearPkt;
+ Request.Flags = 0;
+
+ if (Module != NULL) {
+
+ //
+ // Verify that the caller has clear access to this logfile
+ //
+
+ if (! RtlAreAllAccessesGranted (
+ LogHandle->GrantedAccess,
+ ELF_LOGFILE_CLEAR)) {
+
+ Status = STATUS_ACCESS_DENIED;
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Fill in the request packet
+ //
+
+ Request.Module = Module;
+ Request.LogFile = Module->LogFile;
+ Request.Command = ELF_COMMAND_CLEAR;
+ Request.Status = STATUS_SUCCESS;
+ Request.Pkt.ClearPkt->BackupFileName =
+ (PUNICODE_STRING)BackupFileName;
+
+ //
+ // Call the worker routine to do the operation.
+ //
+
+ ElfPerformRequest (&Request);
+
+ //
+ // Extract status of operation from the request packet
+ //
+
+ Status = Request.Status;
+
+ //
+ // If this was the Security Logfile, and the clear was
+ // successful, then generate an audit.
+ //
+ if ((NT_SUCCESS(Status) &&
+ (_wcsicmp(ELF_SECURITY_MODULE_NAME, Module->LogFile->LogModuleName->Buffer) == 0))) {
+
+ //
+ // We just cleared the security log. Now we want to add
+ // a new event to that log to indicate who did it.
+ //
+ status = RpcImpersonateClient(NULL);
+ if (status != RPC_S_OK) {
+ ElfDbgPrint(("RPC IMPERSONATION FAILED %d\n",status));
+ }
+ else {
+ ElfpGenerateLogClearedEvent(LogHandle);
+ status = RpcRevertToSelf();
+ if (status != RPC_S_OK) {
+ DbgPrint("RPC REVERT TO SELF FAILED %d\n",status);
+ }
+ }
+ }
+
+ }
+ } else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+
+ return (Status);
+
+}
+
+
+NTSTATUS
+ElfrBackupELFW (
+ IN IELF_HANDLE LogHandle,
+ IN PRPC_UNICODE_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrBackupELFW API.
+
+Arguments:
+
+ LogHandle - The context-handle for this module's call.
+
+ BackupFileName - Name of the file to back up the current log file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+ PLOGMODULE Module;
+ ELF_REQUEST_RECORD Request;
+ BACKUP_PKT BackupPkt;
+
+ if (BackupFileName->Length == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ Request.Pkt.BackupPkt = &BackupPkt;
+ Request.Flags = 0;
+
+ //
+ // Find the matching module structure
+ //
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+
+ if (Module != NULL) {
+
+ //
+ // Fill in the request packet
+
+ Request.Module = Module;
+ Request.LogFile = Module->LogFile;
+ Request.Command = ELF_COMMAND_BACKUP;
+ Request.Status = STATUS_SUCCESS;
+ Request.Pkt.BackupPkt->BackupFileName =
+ (PUNICODE_STRING)BackupFileName;
+
+ //
+ // Call the worker routine to do the operation.
+ //
+
+ ElfPerformRequest (&Request);
+
+ //
+ // Extract status of operation from the request packet
+ //
+
+ Status = Request.Status;
+
+ } else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+ return (Status);
+
+}
+
+
+NTSTATUS
+ElfrCloseEL (
+ IN OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrCloseEL API.
+
+Arguments:
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // Call the rundown routine to do all the work
+ //
+
+ IELF_HANDLE_rundown(*LogHandle);
+
+ *LogHandle = NULL; // so RPC knows it's closed
+
+ return (STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+ElfrDeregisterEventSource (
+ IN OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrDeregisterEventSource API.
+
+Arguments:
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if ((*LogHandle)->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // Call the rundown routine to do all the work
+ //
+
+ IELF_HANDLE_rundown(*LogHandle);
+
+ *LogHandle = NULL; // so RPC knows it's closed
+
+ return (STATUS_SUCCESS);
+}
+
+
+
+
+NTSTATUS
+ElfrOpenBELW (
+ IN EVENTLOG_HANDLE_W UNCServerName,
+ IN PRPC_UNICODE_STRING BackupFileName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrOpenBELW API. It creates
+ a module structure $BACKUPnnn where nnn is a unique number for every backup
+ log that is opened. It then calls ElfpOpenELW to actually open the file.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ BackupFileName - Name of the backup log file.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+
+--*/
+{
+
+ NTSTATUS Status;
+ UNICODE_STRING BackupStringW;
+ LPWSTR BackupModuleName;
+ PLOGMODULE pModule;
+ DWORD dwModuleNumber;
+
+//
+// Size of buffer (in bytes) required for a UNICODE string of $BACKUPnnn
+//
+
+#define SIZEOF_BACKUP_MODULE_NAME 64
+
+ UNREFERENCED_PARAMETER(UNCServerName);
+
+ //
+ // Create a unique module name by incrementing a global value
+ //
+
+ BackupModuleName = ElfpAllocateBuffer(SIZEOF_BACKUP_MODULE_NAME);
+ if (BackupModuleName == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ //
+ // Serialize read, increment of the global backup module number.
+ // Note: double-timing the log file list critical section so as to not
+ // require another critical section specifically dedicated to this
+ // operation.
+ //
+
+ RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+ dwModuleNumber = BackupModuleNumber++;
+
+ RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+ swprintf(BackupModuleName, L"$BACKUP%06d", dwModuleNumber);
+ RtlInitUnicodeString(&BackupStringW, BackupModuleName);
+
+ //
+ // Call SetupDataStruct to build the module and log data structures
+ // and actually open the file.
+ //
+ // NOTE: If this call is successful, the Unicode String Buffer for
+ // BackupStringW (otherwise known as BackupModuleName) will be attached
+ // to the LogModule structure, and should not be free'd.
+ //
+ //
+
+ Status = SetUpDataStruct(
+ BackupFileName, // Filename
+ 0, // Max size, it will use actual
+ 0, // retention period, not used for bkup
+ ELF_GUEST_ACCESS_UNRESTRICTED, // restrict guest
+ // access flag, inapplicable for bkup
+ &BackupStringW, // Module name
+ NULL, // Handle to registry, not used
+ ElfBackupLog // Log type
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ ElfpFreeBuffer(BackupModuleName);
+ return(Status);
+ }
+
+ //
+ // Call ElfOpenELW to actually open the log file and get a handle.
+ //
+ if (NT_SUCCESS(Status)) {
+
+ Status = ElfpOpenELW(NULL,
+ (PRPC_UNICODE_STRING) & BackupStringW,
+ NULL,
+ MajorVersion,
+ MinorVersion,
+ LogHandle,
+ ELF_LOGFILE_READ);
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Mark this as a handle for a backup log, so we can clean up
+ // differently when it's closed, as well as disallow clear, backup
+ // and write operations.
+ //
+
+ (*LogHandle)->Flags |= ELF_LOG_HANDLE_BACKUP_LOG;
+
+ }
+ else {
+ //
+ // If we couldn't open the log file, then we need to tear down
+ // the DataStruct we set up with SetUpDataStruct.
+ //
+ pModule = GetModuleStruc ((PUNICODE_STRING)BackupFileName);
+
+ Status = ElfpCloseLogFile (pModule->LogFile, ELF_LOG_CLOSE_BACKUP);
+
+ UnlinkLogModule(pModule);
+ pModule->LogFile->RefCount--;
+ if (pModule->LogFile->RefCount == 0) {
+ UnlinkLogFile(pModule->LogFile); // Unlink the structure
+ RtlDeleteResource ( &pModule->LogFile->Resource );
+ RtlDeleteSecurityObject(&pModule->LogFile->Sd);
+ ElfpFreeBuffer (pModule->LogFile->LogFileName);
+ ElfpFreeBuffer (pModule->LogFile);
+ }
+ ElfpFreeBuffer(pModule->ModuleName);
+ ElfpFreeBuffer(pModule);
+
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+ElfrRegisterEventSourceW (
+ IN EVENTLOG_HANDLE_W UNCServerName,
+ IN PRPC_UNICODE_STRING ModuleName,
+ IN PRPC_UNICODE_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrRegisterEventSourceW API.
+ This routine allocates a structure for the context handle, finds
+ the matching module name and fills in the data. It returns the
+ pointer to the handle structure.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Not used.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+Note:
+
+ For now, just call ElfpOpenELW.
+
+
+--*/
+{
+ PLOGMODULE Module;
+
+ Module = GetModuleStruc ((PUNICODE_STRING)ModuleName);
+
+ return(ElfpOpenELW(UNCServerName, ModuleName, RegModuleName,
+ MajorVersion, MinorVersion, LogHandle, ELF_LOGFILE_WRITE));
+}
+
+NTSTATUS
+ElfrOpenELW (
+ IN EVENTLOG_HANDLE_W UNCServerName,
+ IN PRPC_UNICODE_STRING ModuleName,
+ IN PRPC_UNICODE_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrOpenELW API.
+ This routine allocates a structure for the context handle, finds
+ the matching module name and fills in the data. It returns the
+ pointer to the handle structure.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Not used.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+
+--*/
+{
+ return( ElfpOpenELW (
+ UNCServerName,
+ ModuleName,
+ RegModuleName,
+ MajorVersion,
+ MinorVersion,
+ LogHandle,
+ ELF_LOGFILE_READ));
+}
+
+NTSTATUS
+ElfpOpenELW (
+ IN EVENTLOG_HANDLE_W UNCServerName,
+ IN PRPC_UNICODE_STRING ModuleName,
+ IN PRPC_UNICODE_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle,
+ IN ULONG DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ Looks alot like ElfrOpenELW but also gets passed a DesiredAccess.
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Not used.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+ DesiredAccess - Indicates the access desired for this logfile.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLOGMODULE Module;
+ IELF_HANDLE LogIHandle;
+ BOOL ForSecurityLog = FALSE;
+
+ //
+ // Allocate a new structure for the context handle
+ //
+
+ LogIHandle = (IELF_HANDLE) ElfpAllocateBuffer (
+ sizeof (*LogIHandle)
+ + ModuleName->Length
+ + sizeof (WCHAR)
+ );
+
+ if (LogIHandle) {
+
+ //
+ // Find the module structure in order to pull out the Atom.
+ //
+ // GetModuleStruc *always* succeeds! (returns default if module
+ // not found).
+ //
+
+ Module = GetModuleStruc ((PUNICODE_STRING)ModuleName);
+
+ //
+ // Validate the caller has appropriate access to this logfile.
+ // If this is the security log, then check privilege instead.
+ //
+ if (_wcsicmp(ELF_SECURITY_MODULE_NAME, Module->LogFile->LogModuleName->Buffer) == 0) {
+ ForSecurityLog = TRUE;
+ }
+ Status = ElfpAccessCheckAndAudit(
+ L"EventLog", // SubSystemName
+ L"LogFile", // ObjectTypeName
+ Module->ModuleName, // ObjectName
+ LogIHandle, // Context handle - required?
+ Module->LogFile->Sd, // Security Descriptor
+ DesiredAccess, // Requested Access
+ NULL, // GENERIC_MAPPING
+ ForSecurityLog // Indicates the check is for security log
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ LogIHandle->Atom = Module->ModuleAtom;
+
+ LogIHandle->NameLength = ModuleName->Length + sizeof(WCHAR);
+ RtlMoveMemory( LogIHandle->Name,
+ ModuleName->Buffer,
+ ModuleName->Length
+ );
+
+ LogIHandle->Name[ModuleName->Length / sizeof(WCHAR)] = L'\0';
+
+ LogIHandle->MajorVersion = MajorVersion; // Store the version
+ LogIHandle->MinorVersion = MinorVersion; // of the client
+
+ //
+ // Initialize seek positions and flags to zero.
+ //
+
+ LogIHandle->SeekRecordPos = 0;
+ LogIHandle->SeekBytePos = 0;
+ LogIHandle->Flags = 0;
+
+ //
+ // Link in this structure to the list of context handles
+ //
+
+ LogIHandle->Signature = ELF_CONTEXTHANDLE_SIGN; // DEBUG
+ LinkContextHandle (LogIHandle);
+
+ *LogHandle = LogIHandle; // Set return handle
+ Status = STATUS_SUCCESS; // Set return status
+ }
+ else {
+ ElfpFreeBuffer(LogIHandle);
+ }
+
+ } else {
+
+ Status = STATUS_NO_MEMORY;
+ }
+
+ return (Status);
+
+ UNREFERENCED_PARAMETER(UNCServerName);
+ UNREFERENCED_PARAMETER(RegModuleName);
+}
+
+
+
+NTSTATUS
+w_ElfrReadEL (
+ IN ULONG Flags, // ANSI or UNICODE
+ IN IELF_HANDLE LogHandle,
+ IN ULONG ReadFlags,
+ IN ULONG RecordNumber,
+ IN ULONG NumberOfBytesToRead,
+ IN PBYTE Buffer,
+ OUT PULONG NumberOfBytesRead,
+ OUT PULONG MinNumberOfBytesNeeded
+ )
+
+/*++
+
+Routine Description:
+
+ This is the worker for the ElfrReadEL APIs.
+
+Arguments:
+
+ Same as ElfrReadELW API except that Flags contains an indication
+ of whether this is ANSI or UNICODE.
+
+Return Value:
+
+ Same as the main API.
+
+NOTES:
+
+ We assume that the client-side has validated the flags to ensure that
+ only one type of each bit is set. No checking is done at the server end.
+
+
+--*/
+{
+ NTSTATUS Status;
+ PLOGMODULE Module;
+ ELF_REQUEST_RECORD Request;
+ READ_PKT ReadPkt;
+
+ //
+ // The ELF_HANDLE_INVALID_FOR_READ flag bit would be set if the
+ // file changed underneath this handle.
+ //
+
+ if (LogHandle->Flags & ELF_LOG_HANDLE_INVALID_FOR_READ) {
+ return(STATUS_EVENTLOG_FILE_CHANGED);
+ }
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ Request.Pkt.ReadPkt = &ReadPkt; // Set up read packet in request packet
+
+ //
+ // Find the matching module structure
+ //
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+
+ //
+ // Only continue if the module was found
+ //
+
+ if (Module != NULL) {
+
+ //
+ // Fill in the request packet
+ //
+ Request.Module = Module;
+ Request.Flags = 0;
+ Request.LogFile = Module->LogFile;
+ Request.Command = ELF_COMMAND_READ;
+ Request.Status = STATUS_SUCCESS;
+ Request.Pkt.ReadPkt->MinimumBytesNeeded = *MinNumberOfBytesNeeded;
+ Request.Pkt.ReadPkt->BufferSize = NumberOfBytesToRead;
+ Request.Pkt.ReadPkt->Buffer = (PVOID)Buffer;
+ Request.Pkt.ReadPkt->ReadFlags = ReadFlags;
+ Request.Pkt.ReadPkt->RecordNumber = RecordNumber;
+ Request.Pkt.ReadPkt->LastSeekPos = LogHandle->SeekBytePos;
+ Request.Pkt.ReadPkt->LastSeekRecord = LogHandle->SeekRecordPos;
+ Request.Pkt.ReadPkt->Flags = Flags; // Indicate UNICODE or ANSI
+
+ //
+ // Pass along whether the last read was in a forward or backward
+ // direction (affects how we treat being at EOF). Then reset the
+ // bit in the handle depending on what this read is.
+ //
+
+ if (LogHandle->Flags & ELF_LOG_HANDLE_LAST_READ_FORWARD) {
+ Request.Pkt.ReadPkt->Flags |= ELF_LAST_READ_FORWARD;
+ }
+
+ if (ReadFlags & EVENTLOG_FORWARDS_READ) {
+ LogHandle->Flags |= ELF_LOG_HANDLE_LAST_READ_FORWARD;
+ }
+ else {
+ LogHandle->Flags &= ~(ELF_LOG_HANDLE_LAST_READ_FORWARD);
+ }
+
+
+ //
+ // Perform the operation
+ //
+ ElfPerformRequest( &Request );
+
+ //
+ // Update current seek positions
+ //
+ LogHandle->SeekRecordPos = Request.Pkt.ReadPkt->LastSeekRecord;
+ LogHandle->SeekBytePos = Request.Pkt.ReadPkt->LastSeekPos;
+
+ //
+ // Set up return values
+ //
+ *NumberOfBytesRead = Request.Pkt.ReadPkt->BytesRead;
+ *MinNumberOfBytesNeeded = Request.Pkt.ReadPkt->MinimumBytesNeeded;
+
+ Status = Request.Status;
+
+ } else {
+ Status = STATUS_INVALID_HANDLE;
+ //
+ // Set the NumberOfBytesNeeded to zero since there are no bytes to
+ // transfer.
+ //
+ *NumberOfBytesRead = 0;
+ *MinNumberOfBytesNeeded = 0;
+ }
+
+ return (Status);
+}
+
+
+NTSTATUS
+ElfrReadELW (
+ IN IELF_HANDLE LogHandle,
+ IN ULONG ReadFlags,
+ IN ULONG RecordNumber,
+ IN ULONG NumberOfBytesToRead,
+ IN PBYTE Buffer,
+ OUT PULONG NumberOfBytesRead,
+ OUT PULONG MinNumberOfBytesNeeded
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrReadELW API.
+
+Arguments:
+
+
+
+Return Value:
+
+ Returns an NTSTATUS code, NumberOfBytesRead if the read was successful
+ and MinNumberOfBytesNeeded if the buffer was not big enough.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // Call the worker with the UNICODE flag
+ //
+
+ return(w_ElfrReadEL (
+ ELF_IREAD_UNICODE,
+ LogHandle,
+ ReadFlags,
+ RecordNumber,
+ NumberOfBytesToRead,
+ Buffer,
+ NumberOfBytesRead,
+ MinNumberOfBytesNeeded
+ ));
+
+}
+
+
+
+NTSTATUS
+ElfrReportEventW (
+ IN IELF_HANDLE LogHandle,
+ IN ULONG EventTime,
+ IN USHORT EventType,
+ IN USHORT EventCategory OPTIONAL,
+ IN ULONG EventID,
+ IN USHORT NumStrings,
+ IN ULONG DataSize,
+ IN PRPC_UNICODE_STRING ComputerName,
+ IN PRPC_SID UserSid,
+ IN PRPC_UNICODE_STRING *Strings,
+ IN PBYTE Data,
+ IN USHORT Flags,
+ IN OUT PULONG RecordNumber OPTIONAL,
+ IN OUT PULONG TimeWritten OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrReportEventW API.
+
+Arguments:
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+ PLOGMODULE Module;
+ ELF_REQUEST_RECORD Request;
+ WRITE_PKT WritePkt;
+
+ ULONG RecordLength;
+ ULONG StringOffset, DataOffset;
+ ULONG StringsSize;
+ USHORT i;
+ PVOID EventBuffer;
+ PEVENTLOGRECORD EventLogRecord;
+ PWSTR ReplaceStrings, SrcString;
+ PBYTE BinaryData;
+ PUNICODE_STRING UComputerName;
+ PWSTR UModuleName;
+ ULONG PadSize;
+ ULONG UserSidLength = 0; // Init to zero
+ ULONG UserSidOffset;
+ ULONG ModuleNameLen, ComputerNameLen; // Length in bytes
+ ULONG zero = 0; // For pad bytes
+ LARGE_INTEGER Time;
+ ULONG LogTimeWritten;
+
+ //
+ // These are for Security Auditing to use for paired events. This will
+ // not be implemented in Product 1
+ //
+
+ UNREFERENCED_PARAMETER(RecordNumber);
+ UNREFERENCED_PARAMETER(TimeWritten);
+
+ //
+ // This condition is TRUE iff a backup operator has opened the security
+ // log. In this case deny access, since backup operators are allowed
+ // only backup operation on the security log.
+ //
+
+ if (LogHandle->GrantedAccess & ELF_LOGFILE_BACKUP)
+ {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // Make sure the SID passed in is valid
+ //
+
+ if (ARGUMENT_PRESENT(UserSid)) {
+ if (!IsValidSid(UserSid)) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+ }
+
+ //
+ // Make sure strings are null terminated on this side of the RPC call
+ // (RPC doesn't pass the null terminator)
+
+ for (i = 0; i < NumStrings; i++ ) {
+ if (Strings[i]) {
+ if (Strings[i]->MaximumLength >= Strings[i]->Length +
+ sizeof(WCHAR)) {
+ Strings[i]->Buffer[Strings[i]->Length / sizeof(WCHAR)]
+ = L'\0';
+ }
+ }
+
+ }
+
+ //
+ // Can't write to a backup log
+ //
+
+ if (LogHandle->Flags & ELF_LOG_HANDLE_BACKUP_LOG) {
+ return(STATUS_INVALID_HANDLE);
+ }
+
+ //
+ // Make sure they didn't pass in a null pointer for the data, but tell
+ // me there was something there (I still think RPC should protect me from
+ // this!)
+ //
+
+ if (!Data && DataSize != 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+
+ UComputerName = (PUNICODE_STRING)ComputerName;
+ UModuleName = LogHandle->Name;
+
+ Request.Pkt.WritePkt = &WritePkt; // Set up write packet in request packet
+ Request.Flags = 0;
+
+ //
+ // Find the matching module structure
+ //
+
+ Module = FindModuleStrucFromAtom (LogHandle->Atom);
+
+ if (Module != NULL) {
+
+ //
+ // Generate any additional information needed in the record.
+ //
+ // Info that we have Info to generate
+ // ----------------- ----------------
+ // Modulename UserSidLength
+ // EventType Length
+ // EventID StringOffset
+ // NumStrings DataOffset
+ // Strings PadBytes
+ // DataLength LogTimeWritten
+ // Data
+ // UserSidOffset
+ // UserSid
+ // ComputerName
+ // TimeGenerated
+ //
+
+ // 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
+ );
+
+
+ //
+ // USERSIDLENTGH
+ //
+
+ if (UserSid) {
+ UserSidLength = RtlLengthSid ((PSID)UserSid);
+ }
+
+ //
+ // USERSIDOFFSET
+ //
+ // Extract the lengths from the STRING structure, and take care of
+ // the trailing NULLs.
+ //
+
+ ModuleNameLen = (wcslen(UModuleName) + 1)
+ * sizeof (WCHAR);
+ ComputerNameLen = UComputerName->Length + sizeof(WCHAR);
+
+ UserSidOffset = sizeof(EVENTLOGRECORD)
+ + ModuleNameLen
+ + ComputerNameLen;
+
+ //
+ // STRING OFFSET:
+ //
+
+ StringOffset = UserSidOffset + UserSidLength;
+
+
+ //
+ // Calculate the length of strings so that we can see how
+ // much space is needed for that.
+ //
+
+ StringsSize = 0;
+ for (i=0; i<NumStrings; i++) {
+ if (!Strings[i] || Strings[i]->MaximumLength == 0) {
+ StringsSize += sizeof(WCHAR);
+ }
+ else {
+ StringsSize += Strings[i]->Length + sizeof(WCHAR);
+ }
+ }
+
+ //
+ // DATA OFFSET:
+ //
+
+ DataOffset = StringOffset + StringsSize;
+
+ //
+ // Determine how big a buffer is needed for the eventlog record.
+ //
+
+ RecordLength = 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
+ //
+
+ EventBuffer = ElfpAllocateBuffer(RecordLength);
+
+ if (EventBuffer != NULL) {
+
+ //
+ // Fill up the event record
+ //
+
+ EventLogRecord = (PEVENTLOGRECORD)EventBuffer;
+
+ EventLogRecord->Length = RecordLength;
+ EventLogRecord->TimeGenerated = EventTime;
+ EventLogRecord->Reserved = ELF_LOG_FILE_SIGNATURE;
+ EventLogRecord->TimeWritten = LogTimeWritten;
+ EventLogRecord->EventID = EventID;
+ EventLogRecord->EventType = EventType;
+ EventLogRecord->EventCategory = EventCategory;
+ EventLogRecord->ReservedFlags = Flags;
+ EventLogRecord->ClosingRecordNumber = 0;
+ EventLogRecord->NumStrings = NumStrings;
+ EventLogRecord->StringOffset = StringOffset;
+ EventLogRecord->DataLength = DataSize;
+ EventLogRecord->DataOffset = DataOffset;
+ EventLogRecord->UserSidLength = UserSidLength;
+ EventLogRecord->UserSidOffset = UserSidOffset;
+
+ //
+ // Fill in the variable-length fields
+ //
+
+ //
+ // STRINGS
+ //
+
+ ReplaceStrings = (PWSTR) ( (ULONG)EventLogRecord
+ + (ULONG)StringOffset
+ );
+
+ for (i=0; i < NumStrings; i++) {
+ if (!Strings[i] || Strings[i]->MaximumLength == 0) {
+ *ReplaceStrings = L'\0';
+ ReplaceStrings++;
+ }
+ else {
+ SrcString = (PWSTR)Strings[i]->Buffer;
+ RtlMoveMemory(ReplaceStrings, SrcString,
+ Strings[i]->Length);
+ ReplaceStrings[Strings[i]->Length / sizeof(WCHAR)] =
+ L'\0';
+ ReplaceStrings = (PWSTR)((PBYTE) ReplaceStrings
+ + Strings[i]->Length + sizeof(WCHAR));
+ }
+ }
+
+ //
+ // MODULENAME
+ //
+
+ BinaryData = (PBYTE) EventLogRecord + sizeof(EVENTLOGRECORD);
+ RtlMoveMemory (BinaryData,
+ UModuleName,
+ ModuleNameLen);
+
+ //
+ // COMPUTERNAME
+ //
+
+ ReplaceStrings = (LPWSTR) (BinaryData + ModuleNameLen);
+
+ RtlMoveMemory (ReplaceStrings,
+ UComputerName->Buffer,
+ UComputerName->Length);
+
+ ReplaceStrings[UComputerName->Length / sizeof(WCHAR)] = L'\0';
+
+ //
+ // USERSID
+ //
+
+ BinaryData = (PBYTE) ReplaceStrings + ComputerNameLen;
+
+ ASSERT (BinaryData
+ == ((PBYTE) EventLogRecord) + UserSidOffset);
+
+ RtlMoveMemory (BinaryData,
+ UserSid,
+ UserSidLength);
+
+ //
+ // BINARY DATA
+ //
+
+ BinaryData = (PBYTE) ((ULONG)EventLogRecord + DataOffset);
+ if (Data) {
+ RtlMoveMemory (BinaryData, Data, DataSize);
+ }
+
+ //
+ // PAD - Fill with zeros
+ //
+
+ BinaryData = (PBYTE) ((ULONG)BinaryData + DataSize);
+ RtlMoveMemory (BinaryData, &zero, PadSize);
+
+ //
+ // LENGTH at end of record
+ //
+
+ BinaryData = (PBYTE)((ULONG)BinaryData + PadSize);// Point after pad bytes
+ ((PULONG)BinaryData)[0] = RecordLength;
+
+ //
+ // Make sure we are in the right place
+ //
+
+ ASSERT ((ULONG)BinaryData
+ == (RecordLength + (ULONG)EventLogRecord) - sizeof(ULONG));
+
+ //
+ // Set up request packet.
+ // Link event log record into the request structure.
+ //
+
+ Request.Module = Module;
+ Request.LogFile = Request.Module->LogFile;
+ Request.Command = ELF_COMMAND_WRITE;
+ Request.Pkt.WritePkt->Buffer = (PVOID)EventBuffer;
+ Request.Pkt.WritePkt->Datasize = RecordLength;
+
+ //
+ // Perform the operation
+ //
+
+ ElfPerformRequest( &Request );
+
+ //
+ // Free up the buffer
+ //
+
+ ElfpFreeBuffer(EventBuffer);
+
+ Status = Request.Status; // Set status of WRITE
+
+ } else {
+ Status = STATUS_NO_MEMORY;
+ }
+
+ } else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+ return (Status);
+}
+
+
+
+//
+// ANSI APIs
+//
+
+NTSTATUS
+ElfrClearELFA (
+ IN IELF_HANDLE LogHandle,
+ IN PRPC_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrClearELFA API.
+
+Arguments:
+
+ LogHandle - The context-handle for this module's call.
+
+ BackupFileName - Name of the file to back up the current log file.
+ NULL implies not to back up the file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING BackupFileNameU;
+
+ //
+ // Convert the BackupFileName to a UNICODE STRING and call the
+ // UNICODE API to do the work.
+ //
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING)&BackupFileNameU,
+ (PANSI_STRING)BackupFileName,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = ElfrClearELFW (
+ LogHandle,
+ (PRPC_UNICODE_STRING)&BackupFileNameU
+ );
+
+ RtlFreeUnicodeString (&BackupFileNameU);
+ }
+
+ return (Status);
+
+}
+
+
+
+NTSTATUS
+ElfrBackupELFA (
+ IN IELF_HANDLE LogHandle,
+ IN PRPC_STRING BackupFileName
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrBackupELFA API.
+
+Arguments:
+
+ LogHandle - The context-handle for this module's call.
+
+ BackupFileName - Name of the file to back up the current log file.
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING BackupFileNameU;
+
+ //
+ // Convert the BackupFileName to a UNICODE STRING and call the
+ // UNICODE API to do the work.
+ //
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING)&BackupFileNameU,
+ (PANSI_STRING)BackupFileName,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = ElfrBackupELFW (
+ LogHandle,
+ (PRPC_UNICODE_STRING)&BackupFileNameU
+ );
+
+ RtlFreeUnicodeString (&BackupFileNameU);
+ }
+
+ return (Status);
+
+}
+
+
+NTSTATUS
+ElfrRegisterEventSourceA (
+ IN EVENTLOG_HANDLE_A UNCServerName,
+ IN PRPC_STRING ModuleName,
+ IN PRPC_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrRegisterEventSourceA API.
+ This routine allocates a structure for the context handle, finds
+ the matching module name and fills in the data. It returns the
+ pointer to the handle structure.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Not used.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+Note:
+
+ For now, just call ElfrOpenELA.
+
+
+--*/
+{
+
+ NTSTATUS Status;
+ PLOGMODULE Module;
+ UNICODE_STRING ModuleNameU;
+
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING) &ModuleNameU,
+ (PANSI_STRING) ModuleName,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ Module = GetModuleStruc ((PUNICODE_STRING) & ModuleNameU);
+
+ RtlFreeUnicodeString (& ModuleNameU);
+
+ return(ElfpOpenELA(UNCServerName, ModuleName, RegModuleName,
+ MajorVersion, MinorVersion, LogHandle, ELF_LOGFILE_WRITE));
+
+}
+
+NTSTATUS
+ElfrOpenELA (
+ IN EVENTLOG_HANDLE_A UNCServerName,
+ IN PRPC_STRING ModuleName,
+ IN PRPC_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrOpenEL API.
+ This routine allocates a structure for the context handle, finds
+ the matching module name and fills in the data. It returns the
+ pointer to the handle structure.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Name of module to use to determine the log file.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+
+--*/
+{
+ return (ElfpOpenELA (
+ UNCServerName,
+ ModuleName,
+ RegModuleName,
+ MajorVersion,
+ MinorVersion,
+ LogHandle,
+ ELF_LOGFILE_READ));
+}
+
+NTSTATUS
+ElfpOpenELA (
+ IN EVENTLOG_HANDLE_A UNCServerName,
+ IN PRPC_STRING ModuleName,
+ IN PRPC_STRING RegModuleName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle,
+ IN ULONG DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ Looks alot loke ElfrOpenELA, only this also takes a DesiredAccess parameter.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ ModuleName - Name of the module that is making this call.
+
+ RegModuleName - Name of module to use to determine the log file.
+
+ MajorVersion/MinorVersion - The version of the client.
+
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING ModuleNameU;
+
+ //
+ // Convert the ModuleName and RegModulename to UNICODE STRINGs and call
+ // the UNICODE API to do the work.
+ //
+
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING)&ModuleNameU,
+ (PANSI_STRING)ModuleName,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // We *KNOW* that the UNCServerName is not used
+ // by ElfpOpenELW so we save ourselves some work
+ // and just pass in a NULL.
+ //
+ Status = ElfpOpenELW (
+ (EVENTLOG_HANDLE_W) NULL,
+ (PRPC_UNICODE_STRING)&ModuleNameU,
+ NULL,
+ MajorVersion,
+ MinorVersion,
+ LogHandle,
+ DesiredAccess
+ );
+
+ RtlFreeUnicodeString(&ModuleNameU);
+ }
+
+ return (Status);
+ UNREFERENCED_PARAMETER(UNCServerName);
+}
+
+
+
+
+
+NTSTATUS
+ElfrOpenBELA (
+ IN EVENTLOG_HANDLE_A UNCServerName,
+ IN PRPC_STRING FileName,
+ IN ULONG MajorVersion,
+ IN ULONG MinorVersion,
+ OUT PIELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrOpenBEL API.
+ This routine allocates a structure for the context handle, finds
+ the matching module name and fills in the data. It returns the
+ pointer to the handle structure.
+
+
+Arguments:
+
+ UNCServerName - Not used.
+
+ FileName - Filename of the logfile
+
+ MajorVersion/MinorVersion - The version of the client.
+
+ LogHandle - Pointer to the place where the pointer to the
+ context handle structure will be placed.
+
+Return Value:
+
+ Returns an NTSTATUS code and, if no error, a "handle".
+
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING FileNameU;
+
+ //
+ // Convert the FileName to a UNICODE STRINGs and call
+ // the UNICODE API to do the work.
+ //
+
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING) &FileNameU,
+ (PANSI_STRING) FileName,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // We *KNOW* that the UNCServerName is not used
+ // by ElfrOpenELW so we save ourselves some work
+ // and just pass in a NULL.
+ //
+
+ Status = ElfrOpenBELW (
+ (EVENTLOG_HANDLE_W) NULL,
+ (PRPC_UNICODE_STRING)&FileNameU,
+ MajorVersion,
+ MinorVersion,
+ LogHandle
+ );
+
+ RtlFreeUnicodeString(&FileNameU);
+ }
+
+ return (Status);
+ UNREFERENCED_PARAMETER(UNCServerName);
+
+}
+
+
+
+NTSTATUS
+ElfrReadELA (
+ IN IELF_HANDLE LogHandle,
+ IN ULONG ReadFlags,
+ IN ULONG RecordNumber,
+ IN ULONG NumberOfBytesToRead,
+ IN PBYTE Buffer,
+ OUT PULONG NumberOfBytesRead,
+ OUT PULONG MinNumberOfBytesNeeded
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrReadEL API.
+
+Arguments:
+
+
+
+Return Value:
+
+ Returns an NTSTATUS code, NumberOfBytesRead if the read was successful
+ and MinNumberOfBytesNeeded if the buffer was not big enough.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ //
+ // Call the worker with the UNICODE flag
+ //
+
+ return(w_ElfrReadEL (
+ ELF_IREAD_ANSI,
+ LogHandle,
+ ReadFlags,
+ RecordNumber,
+ NumberOfBytesToRead,
+ Buffer,
+ NumberOfBytesRead,
+ MinNumberOfBytesNeeded
+ ));
+}
+
+
+
+
+NTSTATUS
+ConvertStringArrayToUnicode (
+ PUNICODE_STRING *pUStringArray,
+ PANSI_STRING *Strings,
+ USHORT NumStrings
+ )
+/*++
+
+Routine Description:
+
+ This routine takes an array of STRINGs and generates an array of
+ PUNICODE_STRINGs. The destination array has already been allocated
+ by the caller, but the structures for the UNICODE_STRINGs will need
+ to be allocated by this routine.
+
+Arguments:
+
+ pUStringArray - Array of PUNICODE_STRINGs.
+ Strings - Array of PANSI_STRINGs.
+ NumStrings - Number of elements in the arrays.
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ USHORT i;
+
+ //
+ // For each string passed in, allocate a UNICODE_STRING buffer
+ // and set it to the UNICODE equivalent of the string passed in.
+ //
+ for (i=0; i<NumStrings; i++) {
+
+ if (Strings[i]) {
+
+ pUStringArray[i] = ElfpAllocateBuffer (sizeof (UNICODE_STRING));
+
+ if (pUStringArray[i]) {
+
+ Status = RtlAnsiStringToUnicodeString (
+ pUStringArray[i],
+ (PANSI_STRING)Strings[i],
+ TRUE
+ );
+ } else {
+ Status = STATUS_NO_MEMORY;
+ }
+ } else {
+ pUStringArray[i] = NULL;
+ }
+ if (!NT_SUCCESS(Status))
+ break; // Jump out of loop and return status
+ }
+
+ //
+ // Free any allocations on failure.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+ FreePUStringArray(pUStringArray, (USHORT)(i + 1));
+ }
+
+ return (Status);
+}
+
+
+
+VOID
+FreePUStringArray (
+ PUNICODE_STRING *pUStringArray,
+ USHORT NumStrings
+ )
+/*++
+
+Routine Description:
+
+ This routine takes the PUNICODE_STRING array that was filled in by
+ ConvertStringArrayToUnicode and frees the buffer portion of
+ each unicode string and then the UNICODE structure itseld. It handles
+ the case where the array may not have been filled completely due
+ to insufficient memory.
+
+Arguments:
+
+ pUStringArray - Array of PUNICODE_STRINGs.
+ NumStrings - Number of elements in the array.
+
+Return Value:
+
+ NONE.
+
+--*/
+{
+ USHORT i;
+
+ for (i=0; i<NumStrings; i++) {
+
+ if (pUStringArray[i]) {
+
+ if (pUStringArray[i]->Buffer) {
+ RtlFreeUnicodeString (pUStringArray[i]); // Free the string buffer
+
+ ElfpFreeBuffer (pUStringArray[i]); // Free the structure itself
+ }
+ }
+ }
+}
+
+
+
+NTSTATUS
+ElfrReportEventA (
+ IN IELF_HANDLE LogHandle,
+ IN ULONG Time,
+ IN USHORT EventType,
+ IN USHORT EventCategory OPTIONAL,
+ IN ULONG EventID,
+ IN USHORT NumStrings,
+ IN ULONG DataSize,
+ IN PRPC_STRING ComputerName,
+ IN PRPC_SID UserSid,
+ IN PRPC_STRING *Strings,
+ IN PBYTE Data,
+ IN USHORT Flags,
+ IN OUT PULONG RecordNumber OPTIONAL,
+ IN OUT PULONG TimeWritten OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This is the RPC server entry point for the ElfrReportEventA API.
+
+Arguments:
+
+
+Return Value:
+
+ Returns an NTSTATUS code.
+
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING ComputerNameU;
+ PUNICODE_STRING *pUStringArray;
+
+ //
+ // Convert the ComputerName to a UNICODE STRING and call the
+ // UNICODE API.
+ //
+
+ Status = RtlAnsiStringToUnicodeString (
+ (PUNICODE_STRING)&ComputerNameU,
+ (PANSI_STRING)ComputerName,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ if (NumStrings) {
+
+ pUStringArray = ElfpAllocateBuffer (
+ NumStrings * sizeof (PUNICODE_STRING)
+ );
+
+ //
+ // Convert the array of STRINGs to an array of UNICODE-STRINGs
+ // before calling the unicode API.
+ // We can just use the array of Strings passed in since we
+ // don't need to use it anywhere else.
+ //
+ Status = ConvertStringArrayToUnicode (
+ pUStringArray,
+ (PANSI_STRING *)Strings,
+ NumStrings
+ );
+
+ } else {
+ pUStringArray = NULL; // No strings passed in
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = ElfrReportEventW (
+ LogHandle,
+ Time,
+ EventType,
+ EventCategory,
+ EventID,
+ NumStrings,
+ DataSize,
+ (PRPC_UNICODE_STRING)&ComputerNameU,
+ UserSid,
+ (PRPC_UNICODE_STRING*)pUStringArray,
+ Data,
+ Flags, // Flags - paired event
+ RecordNumber,// RecordNumber | support. not in
+ TimeWritten // TimeWritten _ product 1
+ );
+
+ FreePUStringArray (pUStringArray, NumStrings);
+
+ }
+
+ RtlFreeUnicodeString (&ComputerNameU);
+ }
+ if (pUStringArray)
+ ElfpFreeBuffer (pUStringArray);
+
+ return (Status);
+}
diff --git a/private/eventlog/server/elfcfg.h b/private/eventlog/server/elfcfg.h
new file mode 100644
index 000000000..da8adae47
--- /dev/null
+++ b/private/eventlog/server/elfcfg.h
@@ -0,0 +1,66 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elfcfg.h
+
+Abstract:
+
+ This file contains default settings for the eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 16-Aug-1991
+
+Revision History:
+
+--*/
+
+#ifndef _EVENTDEFAULTS_
+#define _EVENTDEFAULTS_
+
+//
+// Default for the Application log file
+//
+//
+
+#define ELF_DEFAULT_MODULE_NAME ELF_APPLICATION_MODULE_NAME
+#define ELF_APPLICATION_DEFAULT_LOG_FILE L"\\SystemRoot\\system32\\config\\appevent.evt"
+#define ELF_SYSTEM_DEFAULT_LOG_FILE L"\\SystemRoot\\system32\\config\\sysevent.evt"
+#define ELF_SECURITY_DEFAULT_LOG_FILE L"\\SystemRoot\\system32\\config\\secevent.evt"
+#define ELF_DEFAULT_MAX_FILE_SIZE 512*1024
+#define ELF_DEFAULT_RETENTION_PERIOD 1*24*3600
+
+#define ELF_GUEST_ACCESS_UNRESTRICTED 0
+#define ELF_GUEST_ACCESS_RESTRICTED 1
+
+//
+// Maximum size for the buffer that will read the key values from the
+// registry.
+//
+
+#define ELF_MAX_REG_KEY_INFO_SIZE 200
+
+//
+// String defines for the pre-defined nodes in the registry
+// These are used to get to the appropriate nodes.
+//
+
+#define REG_EVENTLOG_NODE_PATH \
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Eventlog"
+
+//
+// String defines for the values for each of the configured pieces of
+// information in the eventlog\logfiles node. These exist per logfile.
+//
+//
+
+#define VALUE_FILENAME L"File"
+#define VALUE_MAXSIZE L"Maxsize"
+#define VALUE_RETENTION L"Retention"
+#define VALUE_RESTRICT_GUEST_ACCESS L"RestrictGuestAccess"
+#define VALUE_DEBUG L"DBFlags"
+
+#endif // ifndef _EVENTDEFAULTS_
diff --git a/private/eventlog/server/elfdata.c b/private/eventlog/server/elfdata.c
new file mode 100644
index 000000000..197d577a1
--- /dev/null
+++ b/private/eventlog/server/elfdata.c
@@ -0,0 +1,218 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ DATA.C
+
+Abstract:
+
+ Thie file contains all the global data elements of the eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 10-Jul-1991
+
+[Environment:]
+
+ User Mode - Win32, except for NTSTATUS returned by some functions.
+
+Revision History:
+
+ 10-Jul-1991 RajenS
+ created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <elfcfg.h>
+
+//
+// Debug flag used to control ElfDbgPrint
+//
+DWORD ElfDebug = 0;
+
+//
+// Handles used for the LPC port.
+//
+HANDLE ElfConnectionPortHandle;
+HANDLE ElfCommunicationPortHandle;
+
+// The heads of various linked lists
+//
+LIST_ENTRY LogFilesHead; // Log files
+
+RTL_CRITICAL_SECTION LogFileCritSec; // Accessing log files
+
+LIST_ENTRY LogModuleHead; // Modules registered for logging
+
+RTL_CRITICAL_SECTION LogModuleCritSec; // Accessing log files
+
+LIST_ENTRY LogHandleListHead; // Context-handles for log handles
+
+RTL_CRITICAL_SECTION LogHandleCritSec; // Accessing log handles
+
+LIST_ENTRY QueuedEventListHead; // Deferred events to write
+
+RTL_CRITICAL_SECTION QueuedEventCritSec; // Accessing the deferred events
+
+LIST_ENTRY QueuedMessageListHead; // Deferred messagebox
+
+RTL_CRITICAL_SECTION QueuedMessageCritSec; // Accessing the deferred mb's
+
+HANDLE ElfDoneEvent; // Event to wait for service termination
+
+//
+// Service-related global data
+//
+
+SERVICE_STATUS_HANDLE ElfServiceStatusHandle;
+
+LPWSTR wname_Eventlogsvc = L"EVENTLOG"; // UNICODE name
+CHAR name_Eventlogsvc[] = "EVENTLOG"; // ASCII name
+
+//
+// The following resource is used to serialize access to the resources
+// of the Eventlog service at the highest level. It is used to make sure
+// that the threads that write/read/clear the log file(s) do not step over
+// the threads that monitor the registry and deal with service control
+// operations.
+//
+// The threads that operate on the log file(s) have Shared access to the
+// resource, since they are further serialized on the file that they are
+// working on.
+//
+// The threads that will modify the internal data structures, or the state
+// of the service, need Exclusive access to the resource so that we can
+// control access to the data structures and log files.
+//
+
+RTL_RESOURCE GlobalElfResource;
+
+//
+// This is used by the Backup API to signify which 4K block of the log it's
+// currently reading. This is used to prevent a writer from overwriting this
+// block while it is reading it. The event is used to let a writer block if
+// it was going to overwrite the current backup block, and get pulsed when
+// the backup thread moves to the next block.
+
+PVOID ElfBackupPointer;
+HANDLE ElfBackupEvent;
+
+//
+// Handle for the LPC thread
+//
+HANDLE LPCThreadHandle;
+
+//
+// Handle for the MessageBox thread
+//
+HANDLE MBThreadHandle;
+
+//
+// Handle and ID for the registry monitor thread
+//
+HANDLE RegistryThreadHandle=NULL;
+DWORD RegistryThreadId;
+
+//
+// Bitmask of things that have been allocated and/or started by the
+// service. When the service terminates, this is what needs to be
+// cleaned.
+//
+ULONG EventFlags; // Keep track of what is allocated
+
+//
+// Record used to indicate the end of the event records in the file.
+//
+ELF_EOF_RECORD EOFRecord = {ELFEOFRECORDSIZE,
+ 0x11111111,
+ 0x22222222,
+ 0x33333333,
+ 0x44444444,
+ FILEHEADERBUFSIZE,
+ FILEHEADERBUFSIZE,
+ 1,
+ 1,
+ ELFEOFRECORDSIZE
+ };
+
+//
+// Default module to use if no match is found, APPLICATION
+//
+
+PLOGMODULE ElfDefaultLogModule;
+
+//
+// Module for the eventlog service itself
+//
+
+PLOGMODULE ElfModule;
+
+//
+// Handle (key) to the event log node in the registry.
+// This is set up by Elfmain().
+//
+
+HANDLE hEventLogNode = NULL; // Initialize to NULL
+
+//
+// Used to create a unigue module name for backup logs
+//
+
+DWORD BackupModuleNumber;
+
+//
+// NT well-known SIDs
+//
+PSVCS_GLOBAL_DATA ElfGlobalData;
+
+//
+// Global anonymous logon sid - used in log ACL's. The only SID allocated
+// specifically by the eventlog service, all others are passed in from
+// the service controller in ElfGlobalData.
+//
+
+PSID AnonymousLogonSid = NULL;
+
+//
+// The local computer name. Used when we generate events ourself.
+//
+
+LPWSTR LocalComputerName = NULL;
+ULONG ComputerNameLength;
+
+//
+// Shutdown Flag
+//
+BOOL EventlogShutdown = FALSE;
+
+HANDLE ElfGlobalSvcRefHandle=NULL;
+
+//
+// This is the string used in the title bar of the Message Box
+// used to display log full messages.
+// GlobalMessageBoxTitle will either point to the default string, or
+// to the string allocated in the format Message function.
+//
+WCHAR DefaultMessageBoxTitle[]=L"Eventlog Service";
+LPWSTR GlobalAllocatedMsgTitle=NULL;
+LPWSTR GlobalMessageBoxTitle=DefaultMessageBoxTitle;
+
+#ifdef _CAIRO_
+
+//
+// The eventlog service links to ALERTSYS.DLL by hand (eventlog.c) after
+// eventlog initialization, since this dll's initialization code requires
+// a running eventlog service.
+//
+
+HINSTANCE ghAlertSysDll = NULL;
+PREPORTALERT gpfReportAlert = NULL;
+
+#endif // _CAIRO_
diff --git a/private/eventlog/server/elfdef.h b/private/eventlog/server/elfdef.h
new file mode 100644
index 000000000..81370a5c6
--- /dev/null
+++ b/private/eventlog/server/elfdef.h
@@ -0,0 +1,478 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elfdef.h
+
+Abstract:
+
+ This file contains defines for the eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 1-Jul-1991
+
+Revision History:
+
+--*/
+
+#ifndef _EVENTDEF_
+#define _EVENTDEF_
+
+//
+// Logfile object specific access type
+//
+#define ELF_LOGFILE_READ 0x0001
+#define ELF_LOGFILE_WRITE 0x0002
+#define ELF_LOGFILE_CLEAR 0x0004
+#define ELF_LOGFILE_START 0x0008
+#define ELF_LOGFILE_STOP 0x000C
+#define ELF_LOGFILE_CONFIGURE 0x0010
+#define ELF_LOGFILE_BACKUP 0x0020 // Set iff a backup operator
+ // opens the security log -
+ // this overrides all other
+ // flags.
+
+#define ELF_LOGFILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ ELF_LOGFILE_READ | \
+ ELF_LOGFILE_WRITE | \
+ ELF_LOGFILE_CLEAR | \
+ ELF_LOGFILE_START | \
+ ELF_LOGFILE_STOP | \
+ ELF_LOGFILE_CONFIGURE)
+
+//
+// Three types of logfiles are defined from a security perspective:
+//
+// ELF_LOGFILE_SECURITY - Only Admins/LocalSystem can RW these files
+// ELF_LOGFILE_SYSTEM - Only Admins/LocalSystem can W these files
+// ELF_LOGFILE_APPLICATION - World can R/W these files
+//
+// System and Security will be SECURE, Application will be NON_SECURE
+//
+
+#define ELF_LOGFILE_SECURITY 0x0000
+#define ELF_LOGFILE_SYSTEM 0x0001
+#define ELF_LOGFILE_APPLICATION 0x0002
+
+//
+// Macro to convert a given file size into one that is "acceptable" for
+// eventlogging. It basically truncates it to a 64K boundary making sure
+// that it is as least 64K
+//
+
+#define ELFFILESIZE(x) ((x & 0xFFFF0000) ? (x & 0xFFFF0000) : 65536)
+
+//
+// Macro for debug prints
+//
+
+#if DBG
+#define ElfDbgPrint(x) if (ElfDebug) DbgPrint x
+#define ElfDbgPrintNC(x) DbgPrint x
+#else
+#define ElfDbgPrint(x)
+#define ElfDbgPrintNC(x)
+#endif
+
+//
+// The largest possible buffer we would need to hold an admin alert
+// information. This primarily depends on the number and length of the
+// replacement strings that would be passed with the message ID.
+//
+
+#define ELF_ADMIN_ALERT_BUFFER_SIZE 256
+
+//
+// Timeout defines.
+//
+
+#define INFINITE_WAIT_TIME -1 // Wait time for events
+#define ELF_GLOBAL_RESOURCE_WAIT 2000 // 2-second timeout for global resource
+
+//
+// Signature placed before each event record in a file. Is used to
+// validate where we are in a file.
+//
+
+#define ELF_RECORD_SIGNATURE 0x654c6652 // ASCII for eLfR
+
+//
+// Size by which to grow a log file until it reaches the max size
+//
+
+#define ELF_DEFAULT_LOG_SIZE 65536
+
+//
+// Bits for whether to take the global resource exclusively or shared.
+//
+
+#define ELF_GLOBAL_SHARED 0x0001
+#define ELF_GLOBAL_EXCLUSIVE 0x0002
+
+//
+// Flag bits to keep track of what resources have been allocated at INIT time
+//
+
+#define ELF_INIT_LOGHANDLE_CRIT_SEC 0x0001
+#define ELF_INIT_GLOBAL_RESOURCE 0x0002
+#define ELF_STARTED_LPC_THREAD 0x0004
+#define ELF_STARTED_REGISTRY_MONITOR 0x0008
+#define ELF_STARTED_RPC_SERVER 0x0010
+#define ELF_INIT_LOGFILE_CRIT_SEC 0x0020
+#define ELF_INIT_WELL_KNOWN_SIDS 0x0040
+#define ELF_INIT_QUEUED_EVENT_CRIT_SEC 0x0080
+#define ELF_INIT_QUEUED_MESSAGE_CRIT_SEC 0x0100
+
+//
+// Security objects
+//
+
+
+#define EVENTLOG_SUBSYSTEM_NAME L"EVENTLOG SERVICE"
+#define LOG_OBJECT_TYPE_NAME L"EVENTLOG LOGFILE OBJECT"
+
+//
+// Structure containing information on each log file
+//
+// ActualMaxFileSize and ConfigMaxFileSize are stored in BYTEs.
+// ActualMaxFileSize is the actual size of the file on the disk.
+// ConfigMaxFileSize is the configured size of the file, which may not
+// be the same as the actual size of the file.
+//
+// CurrentRecordNumber is the next absolute record number to write
+//
+// OldestRecordNumber is the next one to get overwritten
+//
+// Retention time is stored as the number of seconds.
+//
+// BaseAddress points to the physical beginning of the file.
+//
+// ViewSize is ALWAYS the size of the file in bytes.
+//
+// For the Flag bits, see the ELF_LOGFILE_HEADER_xxxx bits defined below.
+//
+
+typedef struct _LOGFILE {
+ LIST_ENTRY FileList;
+ LIST_ENTRY Notifiees; // List of ChangeNotify recipients
+ PUNICODE_STRING LogFileName; // Full path name of log file
+ PUNICODE_STRING LogModuleName; // Name of default module for this log
+ ULONG RefCount; // Number of modules using this file
+ ULONG Flags; // Autowrap, dirty, etc. - See bits below
+ ULONG ConfigMaxFileSize; // Max it can be
+ ULONG ActualMaxFileSize; // How big it is now
+ ULONG NextClearMaxFileSize; // Can't be shrunk on the fly
+ ULONG CurrentRecordNumber;// The next one to be created
+ ULONG OldestRecordNumber; // The next one to overwrite
+ ULONG Retention; // Max. Retention time
+ ULONG NextClearRetention; // they shrank the file when they set this
+ HANDLE FileHandle; // Handle to open file
+ HANDLE SectionHandle; // Memory mapped section handle
+ PVOID BaseAddress; // Map view base address
+ ULONG ViewSize; // Mapped view size
+ ULONG BeginRecord; // Offset of first log record
+ ULONG EndRecord; // Offset of byte after last log record
+ PSECURITY_DESCRIPTOR Sd; // User security object for this log7
+ RTL_RESOURCE Resource;
+} LOGFILE, *PLOGFILE;
+
+//
+// Structure containing information on each module that is registered to
+// log events.
+//
+
+typedef struct _LOGMODULE {
+ LIST_ENTRY ModuleList;
+ PWSTR ModuleName; // Name of module
+ ATOM ModuleAtom; // Atom identifying this module
+ PLOGFILE LogFile; // Log file for this module
+#ifdef _CAIRO_
+ SHORT AlertCategory; // Cairo alert category filter
+ SHORT AlertSeverity; // Cairo alert severity filter
+#endif // _CAIRO_
+} LOGMODULE, *PLOGMODULE;
+
+//
+// Command codes put in the request packets.
+//
+
+#define ELF_COMMAND_READ 1
+#define ELF_COMMAND_WRITE 2
+#define ELF_COMMAND_CLEAR 3
+#define ELF_COMMAND_BACKUP 4
+#define ELF_COMMAND_WRITE_QUEUED 5
+
+//
+// Structures that contain the operation-specific information.
+//
+
+typedef struct _WRITE_PKT {
+ DWORD Datasize; // Size of data in the buffer
+ PVOID Buffer; // Contains filled event log record
+} WRITE_PKT, *PWRITE_PKT;
+
+
+//
+// The following flag bits are used in the READ_PKT Flag field.
+//
+
+#define ELF_IREAD_UNICODE 0x0001
+#define ELF_IREAD_ANSI 0x0002
+#define ELF_LAST_READ_FORWARD 0x0004
+
+typedef struct _READ_PKT {
+ ULONG Flags; // UNICODE or ANSI
+ ULONG BufferSize; // Bytes to read
+ PVOID Buffer; // User's buffer
+ ULONG ReadFlags; // Sequential? Forwards? Random-access? Backwards?
+ ULONG RecordNumber; // Where to start the READ
+ ULONG MinimumBytesNeeded; // For return info if buffer too small
+ ULONG LastSeekPos; // Last seek position in terms of bytes
+ ULONG LastSeekRecord; // Last seek position in terms of records
+ ULONG BytesRead; // Bytes read - for return to caller
+ ULONG RecordsRead;
+} READ_PKT, *PREAD_PKT;
+
+typedef struct _CLEAR_PKT {
+ PUNICODE_STRING BackupFileName; // File to back up current log file (or NULL)
+} CLEAR_PKT, *PCLEAR_PKT;
+
+typedef struct _BACKUP_PKT {
+ PUNICODE_STRING BackupFileName; // File to back up current log file (or NULL)
+} BACKUP_PKT, *PBACKUP_PKT;
+
+//
+// Flags used in the ELF_REQUEST_RECORD
+//
+
+#define ELF_FORCE_OVERWRITE 0x01 // Ignore retention period for this write
+
+//
+// Structure for the packet that contains all the information needed
+// to perform the request.
+//
+
+typedef struct _ELF_REQUEST_RECORD {
+ USHORT Flags;
+ NTSTATUS Status; // To return status of operation
+ PLOGFILE LogFile; // File on which to operate
+ PLOGMODULE Module; // Information on module
+ USHORT Command; // Operation to be performed
+ union {
+ PWRITE_PKT WritePkt;
+ PREAD_PKT ReadPkt;
+ PCLEAR_PKT ClearPkt;
+ PBACKUP_PKT BackupPkt;
+ } Pkt;
+} ELF_REQUEST_RECORD, *PELF_REQUEST_RECORD;
+
+typedef struct _ELF_ALERT_RECORD {
+ DWORD TimeOut;
+ DWORD MessageId;
+ DWORD NumberOfStrings;
+ // array of UNICODE_STRINGs NumberOfStringsLong
+ // each string
+} ELF_ALERT_RECORD, * PELF_ALERT_RECORD;
+
+typedef struct _ELF_MESSAGE_RECORD {
+ DWORD MessageId;
+ DWORD NumberOfStrings;
+ // UNICODE null terminated strings
+} ELF_MESSAGE_RECORD, * PELF_MESSAGE_RECORD;
+
+//
+// Record for the linked list of deferred events (these are raised by the
+// eventlog service itself for writing once the current operation is complete
+//
+
+typedef struct _ELF_QUEUED_EVENT {
+ LIST_ENTRY Next;
+ enum _ELF_QUEUED_EVENT_TYPE {
+ Event,
+ Alert,
+ Message
+ } Type;
+ union _ELF_QUEUED_EVENT_DATA {
+ ELF_REQUEST_RECORD Request;
+ ELF_ALERT_RECORD Alert;
+ ELF_MESSAGE_RECORD Message;
+ } Event;
+} ELF_QUEUED_EVENT, *PELF_QUEUED_EVENT;
+
+//
+// Structure containing information on callers of ElfChangeNotify
+//
+
+typedef struct _NOTIFIEE {
+ LIST_ENTRY Next;
+ IELF_HANDLE Handle;
+ HANDLE Event;
+} NOTIFIEE, *PNOTIFIEE;
+
+
+//
+// Structure that describes the header that is at the beginning of the
+// log files.
+//
+// To see if there are any records in the file, one must subtract the
+// EndOffset from the StartOffset (allowing for the file having wrapped
+// around) and check for a difference of greater than 1.
+//
+// The header size is stored at the beginning and end so that it looks
+// just like any other event log record (the lengths do at any rate).
+//
+
+typedef struct _ELF_LOGFILE_HEADER {
+ ULONG HeaderSize; // Size of this header
+ ULONG Signature; // Signature field
+ ULONG MajorVersion;
+ ULONG MinorVersion;
+ ULONG StartOffset; // Where the first record is located
+ ULONG EndOffset; // The end of the last record + 1
+ ULONG CurrentRecordNumber; // The next record to create
+ ULONG OldestRecordNumber; // The next record to overwrite
+ ULONG MaxSize; // Max. size when file was created
+ ULONG Flags; // DIRTY, etc.
+ ULONG Retention; // Last Retention period.
+ ULONG EndHeaderSize; // Size of this header
+} ELF_LOGFILE_HEADER, *PELF_LOGFILE_HEADER;
+
+#define FILEHEADERBUFSIZE sizeof(ELF_LOGFILE_HEADER)
+#define ELF_LOG_FILE_SIGNATURE 0x654c664c // ASCII for eLfL
+
+//
+// The following flag bits are used in ELF_LOGFILE_HEADER and in the
+// LOGFILE structures' Flag fields.
+//
+
+#define ELF_LOGFILE_HEADER_DIRTY 0x0001 // File has been written to
+#define ELF_LOGFILE_HEADER_WRAP 0x0002 // The file has wrapped
+#define ELF_LOGFILE_LOGFULL_WRITTEN 0x0004 // Written logfull record
+#define ELF_LOGFILE_ARCHIVE_SET 0x0008 // Archive bit flag
+
+
+//
+// Structure that defines the record that identifies the end of the
+// circular log file.
+// This record is used to identify where the last record in the circular
+// buffer is located.
+//
+// NOTE: It is *essential* that this record is of a size that a "normal"
+// event log record can never have. There is code that relies on
+// this fact to detect an "EOF" record.
+//
+// Care must be taken to not disturb the first part of this record. It
+// is used to identify an EOF record. ELFEOFUNIQUEPART must be the
+// number of bytes that are constant.
+//
+
+typedef struct _ELF_EOF_RECORD {
+ ULONG RecordSizeBeginning;
+ ULONG One;
+ ULONG Two;
+ ULONG Three;
+ ULONG Four;
+ ULONG BeginRecord;
+ ULONG EndRecord;
+ ULONG CurrentRecordNumber;
+ ULONG OldestRecordNumber;
+ ULONG RecordSizeEnd;
+} ELF_EOF_RECORD, *PELF_EOF_RECORD;
+
+#define ELFEOFRECORDSIZE sizeof (ELF_EOF_RECORD)
+
+//
+// The following constant is how much of the EOF record is constant, and can
+// be used to identify an EOF record
+//
+
+#define ELFEOFUNIQUEPART 5 * sizeof(ULONG)
+
+//
+// This is used to fill the end of a log record so that the fixed portion
+// of a log record doesn't split the end of the file. It must be less than
+// the minimum size of any valid record
+//
+
+#define ELF_SKIP_DWORD sizeof(ELF_EOF_RECORD) - 1
+
+
+//
+// Time for the sender of a start or stop request to the Eventlog
+// service to wait (in milliseconds) before checking on the
+// Eventlog service again to see if it is done
+//
+
+#define ELF_WAIT_HINT_TIME 20000 // 20 seconds
+
+
+//
+// Flags used by ElfpCloseLogFile
+//
+
+#define ELF_LOG_CLOSE_NORMAL 0x0000
+#define ELF_LOG_CLOSE_FORCE 0x0001
+#define ELF_LOG_CLOSE_BACKUP 0x0002
+
+//
+// Structure used to store information read from the registry
+//
+
+typedef struct _LOG_FILE_INFO {
+ PUNICODE_STRING LogFileName;
+ ULONG MaxFileSize;
+ ULONG Retention;
+ ULONG GuestAccessRestriction;
+} LOG_FILE_INFO, *PLOG_FILE_INFO;
+
+//
+// DEBUG stuff.
+//
+
+//
+// This signature is placed in the context handle for debug purposes only,
+// to track down a bug in freeing the structures.
+//
+
+#define ELF_CONTEXTHANDLE_SIGN 0x654c6648 // ASCII for eLfH
+
+//
+// The different file open (or create) options are based on the type of file.
+// The types, and their meanings are:
+//
+// ElfNormalLog Normal log file, opened for cached io
+// ElfSecurityLog Audit logs, opened for write-thru
+// ElfBackupLog Not an active log file, opened read only, cached
+//
+
+typedef enum _ELF_LOG_TYPE {
+ ElfNormalLog,
+ ElfSecurityLog,
+ ElfBackupLog
+} ELF_LOG_TYPE, *PELF_LOG_TYPE;
+
+
+//
+// Eventlog States (used as return codes)
+//
+
+#define UPDATE_ONLY 0 // no change in state - just send current status.
+#define STARTING 1 // the messenger is initializing.
+#define RUNNING 2 // initialization completed normally - now running
+#define STOPPING 3 // uninstall pending
+#define STOPPED 4 // uninstalled
+#define PAUSED 5 // Paused
+#define PAUSING 6 // In the process of pausing
+#define CONTINUING 7 // In the process of continuing
+
+//
+// Forced Shutdown PendingCodes
+//
+#define PENDING TRUE
+#define IMMEDIATE FALSE
+
+
+#endif // ifndef _EVENTDEF_
diff --git a/private/eventlog/server/elfextrn.h b/private/eventlog/server/elfextrn.h
new file mode 100644
index 000000000..ddf0f1fcd
--- /dev/null
+++ b/private/eventlog/server/elfextrn.h
@@ -0,0 +1,99 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elfextrn.h
+
+Abstract:
+
+ This file contains all the externs for the global variables.
+
+Author:
+
+ Rajen Shah (rajens) 10-Jul-1991
+
+Revision History:
+
+--*/
+
+extern DWORD ElfDebug;
+extern HANDLE ElfConnectionPortHandle;
+extern HANDLE ElfCommunicationPortHandle;
+
+extern PWSTR Computername;
+
+extern LIST_ENTRY LogFilesHead; // Log files
+extern LIST_ENTRY LogModuleHead; // Modules registered for logging
+extern LIST_ENTRY LogHandleListHead; // Context-handles for log handles
+extern LIST_ENTRY QueuedEventListHead; // Deferred events
+extern LIST_ENTRY QueuedMessageListHead; //Deferred Messagebox
+
+extern RTL_CRITICAL_SECTION LogFileCritSec;
+extern RTL_CRITICAL_SECTION LogModuleCritSec;
+extern RTL_CRITICAL_SECTION LogHandleCritSec;
+extern RTL_CRITICAL_SECTION QueuedEventCritSec;
+extern RTL_CRITICAL_SECTION QueuedMessageCritSec;
+
+extern SERVICE_STATUS ElfServiceStatus;
+extern SERVICE_STATUS_HANDLE ElfServiceStatusHandle;
+extern HANDLE ElfDoneEvent;
+
+extern LPWSTR wname_Eventlogsvc;
+extern CHAR name_Eventlogsvc[];
+
+extern RTL_RESOURCE GlobalElfResource;
+
+extern PSID AnonymousLogonSid;
+
+extern PVOID ElfBackupPointer;
+extern HANDLE ElfBackupEvent;
+
+extern HANDLE LPCThreadHandle;
+
+extern HANDLE MBThreadHandle;
+
+extern HANDLE RegistryThreadHandle;
+extern DWORD RegistryThreadId;
+
+extern ULONG EventFlags;
+
+extern ELF_EOF_RECORD EOFRecord;
+
+extern PLOGMODULE ElfDefaultLogModule;
+
+extern PLOGMODULE ElfModule;
+
+extern HANDLE hEventLogNode;
+
+extern DWORD BackupModuleNumber;
+
+extern PSVCS_GLOBAL_DATA ElfGlobalData; // WellKnownSids
+
+
+extern LPWSTR LocalComputerName;
+extern ULONG ComputerNameLength;
+
+extern BOOL EventlogShutdown;
+
+extern HANDLE ElfGlobalSvcRefHandle;
+
+extern WCHAR DefaultMessageBoxTitle[];
+extern LPWSTR GlobalAllocatedMsgTitle;
+extern LPWSTR GlobalMessageBoxTitle;
+
+#ifdef _CAIRO_
+
+//
+// The eventlog service links to ALERTSYS.DLL by hand (eventlog.c) after
+// eventlog initialization, since this dll's initialization code requires
+// a running eventlog service.
+//
+
+typedef LONG (*PREPORTALERT)(PCALERTREPORTRECORD, DWORD);
+
+extern HINSTANCE ghAlertSysDll;
+extern PREPORTALERT gpfReportAlert;
+
+#endif // _CAIRO_
diff --git a/private/eventlog/server/elflpc.c b/private/eventlog/server/elflpc.c
new file mode 100644
index 000000000..36ad8bb83
--- /dev/null
+++ b/private/eventlog/server/elflpc.c
@@ -0,0 +1,774 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ELFLPC.C
+
+Abstract:
+
+ This file contains the routines that deal with the LPC port in the
+ eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 10-Jul-1991
+
+Revision History:
+
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <ntiolog.h> // For IO_ERROR_LOG_[MESSAGE/PACKET]
+#include <elflpc.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <elfextrn.h> // Computername
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <ntdef.h>
+#include <ntstatus.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h> // LocalAlloc
+#include <lmcons.h>
+#include <string.h>
+#include <lmerr.h>
+
+NTSTATUS
+SetUpLPCPort ()
+
+
+/*++
+
+Routine Description:
+
+ This routine sets up the LPC port for the service.
+
+
+Arguments:
+
+
+
+Return Value:
+
+
+
+Note:
+
+
+--*/
+{
+
+ NTSTATUS status;
+ UNICODE_STRING unicodePortName;
+ OBJECT_ATTRIBUTES objectAttributes;
+ PORT_MESSAGE connectionRequest;
+
+
+ ElfDbgPrint (("[ELF] Set up LPC port\n"));
+
+ // Initialize the handles to zero so that we can determine what to do
+ // if we need to clean up.
+
+ ElfConnectionPortHandle = NULL;
+ ElfCommunicationPortHandle = NULL;
+
+ //
+ // Create the LPC port.
+ //
+ RtlInitUnicodeString( &unicodePortName, ELF_PORT_NAME_U );
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &unicodePortName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtCreatePort(
+ &ElfConnectionPortHandle,
+ &objectAttributes,
+ 0,
+ ELF_PORT_MAX_MESSAGE_LENGTH,
+ ELF_PORT_MAX_MESSAGE_LENGTH * 32
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ ElfDbgPrintNC(( "[ELF] Port not created\n" ));
+ goto exit;
+ }
+
+ //
+ // Start listening for the system thread's connection to the port. Note
+ // that it is OK if it happens to call NtConnectPort first--it will
+ // simply block until this call to NtListenPort occurs.
+ //
+ ElfDbgPrint(( "[ELF] Listening to port.\n" ));
+
+ connectionRequest.u1.s1.TotalLength = sizeof(connectionRequest);
+ connectionRequest.u1.s1.DataLength = (CSHORT)0;
+ status = NtListenPort(
+ ElfConnectionPortHandle,
+ &connectionRequest
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ ElfDbgPrintNC(( "[ELF] NtListenPort failed: %X\n", status ));
+ goto exit;
+ }
+
+ //
+ // The system thread has initiated the connection. Accept the connection.
+ //
+ // BUGBUG We need some security check here.
+ //
+ ElfDbgPrint(( "[ELF] Accepting connection to port.\n" ));
+
+ status = NtAcceptConnectPort(
+ &ElfCommunicationPortHandle,
+ NULL, // PortContext
+ &connectionRequest,
+ TRUE, // AcceptConnection
+ NULL, // ServerView
+ NULL // ClientView
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ ElfDbgPrintNC(( "[ELF] NtAcceptConnectPort failed: %X\n", status ));
+ goto exit;
+ }
+
+
+ //
+ // Complete the connection to the port, thereby releasing the system
+ // thread waiting in NtConnectPort.
+ //
+
+ ElfDbgPrint(( "[ELF] Completing connection to port.\n" ));
+
+ status = NtCompleteConnectPort( ElfCommunicationPortHandle );
+
+ if ( !NT_SUCCESS(status) ) {
+ ElfDbgPrintNC(( "[ELF] NtCompleteConnectPort failed: %X\n",
+ status ));
+ goto exit;
+ }
+
+exit:
+
+ //
+ // Close open handles and shut down if necessary.
+ //
+
+ if ( !NT_SUCCESS(status) && ElfConnectionPortHandle != NULL ) {
+ NtClose( ElfConnectionPortHandle );
+ }
+
+ if ( !NT_SUCCESS(status) && ElfCommunicationPortHandle != NULL ) {
+ NtClose( ElfConnectionPortHandle );
+ }
+
+ return(status);
+
+}
+
+
+
+NTSTATUS
+ElfProcessLPCPacket ( PIO_ERROR_LOG_MESSAGE pIoErrorLogMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the packet received from the LPC port and processes it.
+ The only thing that comes in thru the LPC port are packets generated by
+ device drivers and written to this port by the IO Subsystem. The packet is
+ an IO_ERROR_LOG_MESSAGE. The logfile will be system, the module name will
+ be the driver that generated the packet, the SID will always be NULL and
+ there will always be one string, which will be the device name.
+
+ It extracts the information from the LPC packet, and then calls the
+ common routine to do the work of formatting the data into
+ an event record and writing it out to the log file.
+
+
+Arguments:
+
+ pIoErrorLogMessage - Pointer to the data portion of the packet just
+ received through the LPC port.
+
+
+Return Value:
+
+ Status of this operation.
+
+
+--*/
+
+{
+ static PLOGMODULE SystemModule = NULL;
+
+ NTSTATUS status;
+ ELF_REQUEST_RECORD Request;
+ WRITE_PKT WritePkt;
+
+ UNICODE_STRING SystemString;
+ ULONG RecordLength;
+ PEVENTLOGRECORD EventLogRecord;
+ LPWSTR DestinationString, SourceString;
+ PBYTE BinaryData;
+ ULONG PadSize;
+ LARGE_INTEGER Time;
+ ULONG TimeWritten;
+ PULONG pEndLength;
+ ULONG i = 0;
+ PWCHAR pwch;
+ PWCHAR pwStart;
+ PWCHAR pwEnd;
+ ULONG StringLength;
+
+ try {
+
+ //
+ // Validate the packet, First make sure there are the correct
+ // number of NULL terminated strings, and remember the
+ // total number of bytes to copy
+ //
+
+ pwStart = pwch = (PWCHAR) ((PBYTE) pIoErrorLogMessage +
+ pIoErrorLogMessage->EntryData.StringOffset);
+
+ pwEnd = (PWCHAR) ((PBYTE) pIoErrorLogMessage +
+ pIoErrorLogMessage->Size);
+
+ while (pwch < pwEnd &&
+ i < pIoErrorLogMessage->EntryData.NumberOfStrings) {
+ if (*pwch == L'\0') {
+ i++;
+ }
+ pwch++;
+ }
+ StringLength = (pwch - pwStart) * sizeof(WCHAR);
+
+ //
+ // Now make sure everything in the packet is true
+ //
+
+ if ((i != pIoErrorLogMessage->EntryData.NumberOfStrings)
+
+ ||
+
+ (pIoErrorLogMessage->DriverNameOffset +
+ pIoErrorLogMessage->DriverNameLength >=
+ pIoErrorLogMessage->Size)
+
+ ||
+
+
+ (pIoErrorLogMessage->EntryData.StringOffset >=
+ pIoErrorLogMessage->Size)
+
+ ||
+
+ (FIELD_OFFSET(IO_ERROR_LOG_MESSAGE, EntryData) +
+ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) +
+ pIoErrorLogMessage->EntryData.DumpDataSize >=
+ pIoErrorLogMessage->Size)) {
+
+ //
+ // It's a bad packet, log it and return
+ //
+
+ ElfDbgPrintNC(("[ELF] Bad packet from LPC port\n"));
+ ElfpCreateElfEvent(EVENT_BadDriverPacket,
+ EVENTLOG_ERROR_TYPE,
+ 0, // EventCategory
+ 0, // NumberOfStrings
+ NULL, // Strings
+ pIoErrorLogMessage, // Data
+ pIoErrorLogMessage->Size, // Datalength
+ 0 // flags
+ );
+ return(STATUS_UNSUCCESSFUL);
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+
+ //
+ // It's a bad packet, log it and return
+ //
+
+ ElfDbgPrintNC(("[ELF] Bad packet from LPC port\n"));
+ ElfpCreateElfEvent(EVENT_BadDriverPacket,
+ EVENTLOG_ERROR_TYPE,
+ 0, // EventCategory
+ 0, // NumberOfStrings
+ NULL, // Strings
+ pIoErrorLogMessage, // Data
+ pIoErrorLogMessage->Size, // Datalength
+ 0 // flags
+ );
+ return(STATUS_UNSUCCESSFUL);
+
+ }
+
+ //
+ // We're going to need this everytime, so just get it once
+ //
+
+ if (!SystemModule) {
+
+ //
+ // Get the system module to log driver events
+ //
+
+ RtlInitUnicodeString(&SystemString, ELF_SYSTEM_MODULE_NAME);
+ SystemModule = GetModuleStruc (&SystemString);
+ ASSERT(SystemModule); // GetModuleStruc never returns NULL
+
+ }
+
+ //
+ // The packet should be an IO_ERROR_LOG_MESSAGE
+ //
+
+ ASSERT(pIoErrorLogMessage->Type == IO_TYPE_ERROR_MESSAGE);
+
+ //
+ // Set up write packet in request packet
+ //
+
+ Request.Pkt.WritePkt = &WritePkt;
+ Request.Flags = 0;
+
+ //
+ // Generate any additional information needed in the record.
+ //
+
+ // TIMEWRITTEN
+ // 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,
+ &TimeWritten
+ );
+
+ //
+ // Determine how big a buffer is needed for the eventlog record.
+ //
+
+ RecordLength = sizeof(EVENTLOGRECORD)
+ + ComputerNameLength // computername
+ + 2 * sizeof(WCHAR) // term's
+ + pIoErrorLogMessage->Size
+ - FIELD_OFFSET(IO_ERROR_LOG_MESSAGE, EntryData)
+ + sizeof(RecordLength); // final len
+
+ //
+ // 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
+ //
+
+ EventLogRecord = (PEVENTLOGRECORD) ElfpAllocateBuffer(RecordLength);
+
+ if (EventLogRecord != (PEVENTLOGRECORD) NULL) {
+
+ //
+ // Fill up the event record
+ //
+
+ EventLogRecord->Length = RecordLength;
+ RtlTimeToSecondsSince1970(
+ &pIoErrorLogMessage->TimeStamp,
+ &EventLogRecord->TimeGenerated
+ );
+ EventLogRecord->Reserved = ELF_LOG_FILE_SIGNATURE;
+ EventLogRecord->TimeWritten = TimeWritten;
+ EventLogRecord->EventID = pIoErrorLogMessage->EntryData.ErrorCode;
+
+ // set EventType based on the high order nibble of
+ // pIoErrorLogMessage->EntryData.ErrorCode
+
+ if (NT_INFORMATION(pIoErrorLogMessage->EntryData.ErrorCode)) {
+
+ EventLogRecord->EventType = EVENTLOG_INFORMATION_TYPE;
+
+ }
+ else if (NT_WARNING(pIoErrorLogMessage->EntryData.ErrorCode)) {
+
+ EventLogRecord->EventType = EVENTLOG_WARNING_TYPE;
+
+ }
+ else if (NT_ERROR(pIoErrorLogMessage->EntryData.ErrorCode)) {
+
+ EventLogRecord->EventType = EVENTLOG_ERROR_TYPE;
+
+ }
+ else {
+
+ //
+ // Unknown, set to error
+ //
+
+ EventLogRecord->EventType = EVENTLOG_ERROR_TYPE;
+
+ }
+
+ EventLogRecord->NumStrings =
+ pIoErrorLogMessage->EntryData.NumberOfStrings;
+ EventLogRecord->EventCategory =
+ pIoErrorLogMessage->EntryData.EventCategory;
+ EventLogRecord->StringOffset = sizeof(EVENTLOGRECORD) +
+ pIoErrorLogMessage->DriverNameLength + ComputerNameLength;
+ EventLogRecord->DataLength =
+ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) +
+ pIoErrorLogMessage->EntryData.DumpDataSize;
+ EventLogRecord->DataOffset = EventLogRecord->StringOffset +
+ StringLength;
+ EventLogRecord->UserSidLength = 0;
+ EventLogRecord->UserSidOffset = 0;
+
+ //
+ // Fill in the variable-length fields
+
+ // MODULENAME
+ //
+ // Use the driver name as the module name, since it's location is
+ // described by an offset from the start of the IO_ERROR_LOG_MESSAGE
+ // turn it into a pointer
+ //
+
+ DestinationString = (LPWSTR)((LPBYTE)EventLogRecord +
+ sizeof(EVENTLOGRECORD));
+ SourceString = (LPWSTR)((LPBYTE) pIoErrorLogMessage +
+ pIoErrorLogMessage->DriverNameOffset);
+
+ RtlMoveMemory(DestinationString, SourceString,
+ pIoErrorLogMessage->DriverNameLength);
+
+ //
+ // Make sure it's NULL terminated
+ //
+
+ DestinationString = (LPWSTR)((LPBYTE) DestinationString +
+ pIoErrorLogMessage->DriverNameLength) - 1;
+ if (*DestinationString != L'\0') {
+ *(++DestinationString) = L'\0';
+ }
+
+ DestinationString++;
+
+ // COMPUTERNAME
+ //
+
+ RtlMoveMemory(DestinationString, LocalComputerName,
+ ComputerNameLength);
+
+ //
+ // Make sure it's NULL terminated
+ //
+
+ DestinationString = (LPWSTR)((LPBYTE) DestinationString +
+ ComputerNameLength) - 1;
+ if (*DestinationString != L'\0') {
+ *(++DestinationString) = L'\0';
+ }
+
+ DestinationString++;
+
+ // STRING
+ //
+
+ SourceString = pwStart;
+
+ RtlMoveMemory(DestinationString, SourceString, StringLength);
+
+ DestinationString += (StringLength / 2);
+
+ // BINARY DATA
+ //
+ BinaryData = (LPBYTE) DestinationString;
+
+
+ RtlMoveMemory (BinaryData, & pIoErrorLogMessage->EntryData,
+ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) +
+ pIoErrorLogMessage->EntryData.DumpDataSize);
+
+ // LENGTH at end of record
+ //
+
+ pEndLength = (PULONG)((LPBYTE) EventLogRecord + RecordLength -
+ sizeof(ULONG));
+ *pEndLength = RecordLength;
+
+ //
+ // Set up request packet.
+ // Link event log record into the request structure.
+ //
+
+ Request.Module = SystemModule;
+ Request.LogFile = Request.Module->LogFile;
+ Request.Command = ELF_COMMAND_WRITE;
+ Request.Pkt.WritePkt->Buffer = (PVOID)EventLogRecord;
+ Request.Pkt.WritePkt->Datasize = RecordLength;
+
+ //
+ // Perform the operation
+ //
+
+ ElfPerformRequest( &Request );
+
+ //
+ // Free up the buffer
+ //
+
+ ElfpFreeBuffer(EventLogRecord );
+
+ status = Request.Status; // Set status of WRITE
+
+ } else {
+ status = STATUS_NO_MEMORY;
+ }
+
+ return (status);
+}
+
+
+
+NTSTATUS
+ElfProcessLPCCalls (
+ )
+
+/*++
+
+Routine Description:
+
+ This routine waits for messages to come through the LPC port to
+ the system thread. When one does, it calls the appropriate routine to
+ handle the API, then replies to the system thread indicating that the
+ call has completed if the message was a request, if it was a datagram,
+ it just waits for the next message.
+
+Arguments:
+
+
+Return Value:
+
+--*/
+
+{
+ NTSTATUS status;
+
+ BOOL SendReply = FALSE;
+
+ ELF_REPLY_MESSAGE replyMessage;
+ PELFIOPORTMSG receiveMessage;
+
+ //
+ // Loop dispatching API requests.
+ //
+
+ receiveMessage = ElfpAllocateBuffer(ELF_PORT_MAX_MESSAGE_LENGTH +
+ sizeof(PORT_MESSAGE));
+ if (!receiveMessage) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ while ( TRUE ) {
+
+ //
+ // On the first call to NtReplyWaitReceivePort, don't send a
+ // reply since there's nobody to reply to. However, on subsequent
+ // calls the reply to the message from the prior time if that message
+ // wasn't a LPC_DATAGRAM
+ //
+
+ status = NtReplyWaitReceivePort(
+ ElfConnectionPortHandle,
+ NULL, // PortContext
+ (PPORT_MESSAGE)( SendReply ? &replyMessage : NULL),
+ (PPORT_MESSAGE) receiveMessage
+ );
+
+ if ( !NT_SUCCESS(status) ) {
+ ElfDbgPrintNC(( "[ELF] ElfProcessLPCCalls: NtReplyWaitReceivePort failed: %X\n",
+ status ));
+ return status;
+ }
+
+ ElfDbgPrint(( "[ELF] ElfProcessLPCCalls: received message\n" ));
+
+ //
+ // Take the record received and perform the operation. Strip off
+ // the PortMessage and just send the packet
+ //
+
+
+ //
+ // Set up the response message to be sent on the next call to
+ // NtReplyWaitReceivePort if this wasn't a datagram.
+ // 'status' contains the status to return from this call.
+ // Only process messages that are LPC_REQUEST or LPC_DATAGRAM
+ //
+
+ if (receiveMessage->PortMessage.u2.s2.Type == LPC_REQUEST) {
+ status = ElfProcessLPCPacket (& receiveMessage->IoErrorLogMessage);
+
+ replyMessage.PortMessage.u1.s1.DataLength =
+ sizeof(replyMessage) - sizeof(PORT_MESSAGE);
+ replyMessage.PortMessage.u1.s1.TotalLength = sizeof(replyMessage);
+ replyMessage.PortMessage.u2.ZeroInit = 0;
+ replyMessage.PortMessage.ClientId =
+ receiveMessage->PortMessage.ClientId;
+ replyMessage.PortMessage.MessageId =
+ receiveMessage->PortMessage.MessageId;
+ replyMessage.Status = status;
+
+ SendReply = TRUE;
+ }
+ else if (receiveMessage->PortMessage.u2.s2.Type == LPC_DATAGRAM) {
+ status = ElfProcessLPCPacket (& receiveMessage->IoErrorLogMessage);
+ SendReply = FALSE;
+ }
+ else {
+ //
+ // We received a message type we didn't expect, probably due to
+ // error. BUGBUG - write an event
+ //
+
+ ElfDbgPrintNC(("[ELF] Unexpected message type received on LPC port\n"));
+ ElfDbgPrintNC(("[ELF] Messaage type = %d\n",
+ receiveMessage->PortMessage.u2.s2.Type));
+
+ }
+ }
+
+} // ElfProcessLPCCalls
+
+
+
+DWORD
+MainLPCThread (
+ LPVOID LPCThreadParm
+ )
+
+/*++
+
+Routine Description:
+
+ This is the main thread that monitors the LPC port from the I/O system.
+ It takes care of creating the LPC port, and waiting for input, which
+ it then transforms into the right operation on the event log.
+
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ ElfDbgPrint(( "[ELF] Inside LPC thread\n" ));
+
+ Status = SetUpLPCPort();
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Loop forever. This thread will be killed when the service terminates.
+ //
+ while (TRUE) {
+
+ Status = ElfProcessLPCCalls ();
+
+ }
+
+ }
+ ElfDbgPrintNC (("[ELF] Error from SetUpLPCPort. Status = %lx\n", Status));
+
+ return (Status);
+
+ UNREFERENCED_PARAMETER ( LPCThreadParm );
+}
+
+
+
+BOOL
+StartLPCThread ()
+
+/*++
+
+Routine Description:
+
+ This routine starts up the thread that monitors the LPC port.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ TRUE if thread creation succeeded, FALSE otherwise.
+
+Note:
+
+
+--*/
+{
+ DWORD error;
+ DWORD ThreadId;
+
+ ElfDbgPrint(( "[ELF] Start up the LPC thread\n" ));
+
+ //
+ // Start up the actual thread.
+ //
+
+ LPCThreadHandle = CreateThread(
+ NULL, // lpThreadAttributes
+ 4096, // dwStackSize
+ MainLPCThread, // lpStartAddress
+ NULL, // lpParameter
+ 0L, // dwCreationFlags
+ &ThreadId // lpThreadId
+ );
+
+ if ( LPCThreadHandle == NULL ) {
+ error = GetLastError();
+ ElfDbgPrintNC(( "[ELF]: LPCThread - CreateThread failed: %ld\n", error ));
+ return (FALSE);
+ }
+ return (TRUE);
+}
diff --git a/private/eventlog/server/elflpc.h b/private/eventlog/server/elflpc.h
new file mode 100644
index 000000000..e11f526cc
--- /dev/null
+++ b/private/eventlog/server/elflpc.h
@@ -0,0 +1,54 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elflpc.h
+
+Abstract:
+
+ This file contains defines for LPC for the eventlog service
+
+Author:
+
+ Rajen Shah (rajens) 10-Jul-1991
+
+Revision History:
+
+--*/
+
+#ifndef _ELFLPC_
+#define _ELFLPC_
+
+// Name of the LPC port for kernel objects to communicate with the eventlog
+// service
+#define ELF_PORT_NAME "\\ErrorLogPort"
+#define ELF_PORT_NAME_U L"\\ErrorLogPort"
+
+//
+// Max size of data sent to the eventlogging service through the LPC port.
+//
+
+#define ELF_PORT_MAX_MESSAGE_LENGTH IO_ERROR_LOG_MESSAGE_LENGTH
+
+//
+// Structure that is passed in from the system thread to the LPC port
+//
+
+typedef struct _ELFIOPORTMSG {
+ PORT_MESSAGE PortMessage;
+ IO_ERROR_LOG_MESSAGE IoErrorLogMessage;
+} ELFIOPORTMSG, *PELFIOPORTMSG;
+
+//
+// Structure for the message as a reply from the eventlogging service to
+// the LPC client.
+//
+
+typedef struct _ELF_REPLY_MESSAGE {
+ PORT_MESSAGE PortMessage;
+ NTSTATUS Status;
+} ELF_REPLY_MESSAGE, *PELF_REPLY_MESSAGE;
+
+#endif // ifndef _ELFLPC_
diff --git a/private/eventlog/server/elfproto.h b/private/eventlog/server/elfproto.h
new file mode 100644
index 000000000..a6a28fa42
--- /dev/null
+++ b/private/eventlog/server/elfproto.h
@@ -0,0 +1,384 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ elfproto.h
+
+Abstract:
+
+ This file contains the prototypes for the Eventlog service.
+
+Author:
+
+ Rajen Shah (rajens) 12-Aug-1991
+
+Revision History:
+
+--*/
+
+#ifndef _ELFPROTO_
+#define _ELFPROTO
+
+//
+// Other prototypes
+//
+PVOID
+ElfpAllocateBuffer (ULONG size);
+
+VOID
+ElfpFreeBuffer (PVOID BufPtr);
+
+VOID
+ElfPerformRequest( PELF_REQUEST_RECORD Request);
+
+
+PLOGMODULE
+GetModuleStruc( PUNICODE_STRING ModuleName );
+
+PLOGMODULE
+FindModuleStrucFromAtom ( ATOM Atom );
+
+
+VOID
+ElfControlResponse(
+ DWORD);
+
+VOID
+ElfAnnounceServiceStatus();
+
+VOID
+Elfmain (
+ DWORD argc,
+ LPWSTR argv[]
+ );
+
+VOID
+ElfPrepareForPause();
+
+VOID
+ElfPrepareForContinue();
+
+VOID
+IELF_HANDLE_rundown(
+ IELF_HANDLE ElfHandle
+ );
+
+VOID
+LinkContextHandle(
+ IELF_HANDLE LogHandle
+ );
+
+VOID
+UnlinkContextHandle (
+ IELF_HANDLE LogHandle
+ );
+
+VOID
+LinkLogModule (
+ PLOGMODULE pLogModule,
+ ANSI_STRING * pModuleNameA
+ );
+
+VOID
+UnlinkLogModule (
+ PLOGMODULE pLogModule
+ );
+
+VOID
+LinkLogFile (
+ PLOGFILE pLogFile
+ );
+
+VOID
+UnlinkLogFile (
+ PLOGFILE pLogFile
+ );
+
+VOID
+GetGlobalResource (DWORD Type
+ );
+
+VOID
+ReleaseGlobalResource();
+
+NTSTATUS
+SetUpDataStruct (
+ PUNICODE_STRING LogFileName,
+ ULONG MaxFileSize,
+ ULONG Retention,
+ ULONG GuestAccessRestriction,
+ PUNICODE_STRING ModuleName,
+ HANDLE hLogFile,
+ ELF_LOG_TYPE LogType
+ );
+
+NTSTATUS
+SetUpModules (
+ HANDLE hLogFile,
+ PLOGFILE pLogFile,
+ BOOLEAN bAllowDupes
+ );
+
+BOOL
+StartLPCThread ();
+
+VOID
+StopLPCThread ();
+
+BOOL
+ElfStartRegistryMonitor ();
+
+VOID
+StopRegistryMonitor ();
+
+NTSTATUS
+ElfImpersonateClient(
+ VOID
+ );
+
+NTSTATUS
+ElfRevertToSelf(
+ VOID
+ );
+
+NTSTATUS
+ReadRegistryInfo (
+ HANDLE hLogFiles,
+ PUNICODE_STRING SubKeyName,
+ PLOG_FILE_INFO LogFileInfo
+ );
+
+NTSTATUS
+ElfOpenLogFile (
+ PLOGFILE pLogFile,
+ ELF_LOG_TYPE LogType
+ );
+
+NTSTATUS
+ElfpCloseLogFile (
+ PLOGFILE pLogFile,
+ DWORD Flags
+ );
+
+BOOL
+ValidFilePos (
+ PVOID Position,
+ PVOID BeginningRecord,
+ PVOID EndingRecord,
+ PVOID PhysicalEOF,
+ PVOID BaseAddress
+ );
+
+VOID
+ElfpCleanUp (
+ ULONG EventFlags
+ );
+
+NTSTATUS
+ElfpCopyFile (
+ IN HANDLE SourceHandle,
+ IN PUNICODE_STRING TargetFileName
+ );
+
+VOID
+FreeModuleAndLogFileStructs (VOID);
+
+NTSTATUS
+ElfpFlushFiles (VOID);
+
+
+VOID
+InvalidateContextHandlesForLogFile (
+ PLOGFILE pLogFile
+ );
+
+VOID
+FixContextHandlesForRecord (
+ DWORD RecordOffset,
+ DWORD NewRecordOffset
+ );
+
+PLOGFILE
+FindLogFileFromName (
+ PUNICODE_STRING LogFileName
+ );
+
+BOOL
+SendAdminAlert (
+ ULONG MessageID,
+ ULONG NumStrings,
+ UNICODE_STRING *pStrings
+ );
+
+PVOID
+NextRecordPosition (
+ ULONG ReadFlags,
+ PVOID CurrPosition,
+ ULONG CurrRecordLength,
+ PVOID BeginRecord,
+ PVOID EndRecord,
+ PVOID PhysicalEOF,
+ PVOID PhysStart
+ );
+
+VOID
+NotifyChange (
+ PLOGFILE pLogFile
+ );
+
+
+VOID
+WriteQueuedEvents (
+ );
+
+VOID
+FlushQueuedEvents (
+ );
+
+VOID
+PerformWriteRequest ( PELF_REQUEST_RECORD Request
+ );
+
+NTSTATUS
+ElfpCreateLogFileObject(
+ PLOGFILE LogFile,
+ DWORD Type,
+ ULONG GuestAccessRestriction
+ );
+
+VOID
+ElfpDeleteLogFileObject(
+ PLOGFILE LogFile
+ );
+
+VOID
+ElfpCloseAudit(
+ IN LPWSTR SubsystemName,
+ IN IELF_HANDLE ContextHandle
+ );
+
+NTSTATUS
+ElfpAccessCheckAndAudit(
+ IN LPWSTR SubsystemName,
+ IN LPWSTR ObjectTypeName,
+ IN LPWSTR ObjectName,
+ IN OUT IELF_HANDLE ContextHandle,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN ACCESS_MASK DesiredAccess,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN BOOL ForSecurityLog
+ );
+
+NTSTATUS
+ElfCreateWellKnownSids(
+ VOID
+ );
+
+VOID
+ElfFreeWellKnownSids(
+ VOID
+ );
+
+NTSTATUS
+ElfCreateAndSetSD(
+ IN PRTL_ACE_DATA AceData,
+ IN ULONG AceCount,
+ IN PSID OwnerSid OPTIONAL,
+ IN PSID GroupSid OPTIONAL,
+ OUT PSECURITY_DESCRIPTOR *NewDescriptor
+ );
+
+NTSTATUS
+ElfCreateUserSecurityObject(
+ IN PRTL_ACE_DATA AceData,
+ IN ULONG AceCount,
+ IN PSID OwnerSid,
+ IN PSID GroupSid,
+ IN BOOLEAN IsDirectoryObject,
+ IN PGENERIC_MAPPING GenericMapping,
+ OUT PSECURITY_DESCRIPTOR *NewDescriptor
+ );
+
+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
+ );
+
+
+VOID
+ElfpCreateQueuedAlert(
+ DWORD MessageId,
+ DWORD NumberOfStrings,
+ LPWSTR Strings[]
+ );
+
+VOID
+ElfpCreateQueuedMessage(
+ DWORD MessageId,
+ DWORD NumberOfStrings,
+ LPWSTR Strings[]
+ );
+
+DWORD
+ElfStatusUpdate(
+ IN DWORD NewState
+ );
+
+DWORD
+GetElState (
+ VOID
+ );
+
+VOID
+ElfpGenerateLogClearedEvent(
+ IELF_HANDLE LogHandle
+ );
+
+VOID
+ElInitStatus(
+ VOID
+ );
+
+VOID
+ElCleanupStatus(
+ VOID
+ );
+
+DWORD
+ElfBeginForcedShutdown(
+ IN BOOL PendingCode,
+ IN DWORD ExitCode,
+ IN DWORD ServiceSpecificCode
+ );
+
+#ifdef _CAIRO_
+
+BOOL
+GetSourceAlertFilterFromRegistry(
+ HANDLE hKeyLogFile,
+ UNICODE_STRING * pswszSourceName,
+ SHORT * psCategoryFilter,
+ SHORT * psSeverityFilter);
+
+BOOL
+TestFilter(
+ WORD NTEventType,
+ SHORT sAlertSeverity);
+
+HRESULT
+RaiseCairoAlert(
+ PLOGMODULE pLogModule,
+ EVENTLOGRECORD * pEventLogRecord);
+
+#endif // _CAIRO_
+
+#endif // ifndef _ELFPROTO_
diff --git a/private/eventlog/server/elfrpc.c b/private/eventlog/server/elfrpc.c
new file mode 100644
index 000000000..0aa37dbeb
--- /dev/null
+++ b/private/eventlog/server/elfrpc.c
@@ -0,0 +1,216 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ELFRPC.C
+
+Abstract:
+
+ This file contains the routines that handle the RPC calls to the
+ Eventlog service via the Elf APIs.
+
+Author:
+
+ Rajen Shah (rajens) 16-Jul-1991
+
+Revision History:
+
+ 15-Feb-1995 MarkBl
+ Unlink the ElfHandle *prior* to unlinking the module. Otherwise,
+ if another thread happens to coincidentally be in the routine,
+ FindModuleStrucFromAtom, it's not going to get a hit for the
+ module atom.
+
+ 18-May-1994 Danl
+ IELF_HANDLE_rundown: If the eventlog has been shutdown, then
+ we want to skip the code in this routine because most of the
+ resources will have been free'd.
+
+ 31-Jan-1994 Danl
+ IELF_HANDLE_rundown: Notifiee structure was being free'd and then
+ referenced when it's handle was removed from the list. Now this
+ is fixed so it advances to the next Notifiee in the list BEFORE the
+ buffer is free'd.
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+
+
+
+extern DWORD ElState;
+
+
+VOID
+IELF_HANDLE_rundown(
+ IELF_HANDLE ElfHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by the server RPC runtime to run down a
+ Context Handle and to free any allocated data. It also does all
+ the work for ElfrCloseEL.
+
+ It has to undo whatever was done in ElfrOpenEventLog in terms of
+ allocating memory.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+--*/
+
+{
+ PLOGMODULE pModule;
+ NTSTATUS Status;
+ PNOTIFIEE Notifiee;
+ PNOTIFIEE NotifieeToDelete;
+
+ //
+ // Generate an Audit if neccessary
+ //
+ ElfpCloseAudit(L"EventLog",ElfHandle);
+
+ //
+ // If the eventlog service is stopped or in the process of
+ // stopping, then we just want to ignore this rundown and return.
+ //
+ // Note, we don't bother calling GetElState() because that uses
+ // a critical section which may not exist anymore is the
+ // eventlog service has been shutdown.
+ //
+ // The eventlog isn't designed to be shutdown (except when the
+ // system is shutdown), so it isn't real good at cleaning up
+ // its resources.
+ //
+ if (ElState == STOPPING || ElState == STOPPED) {
+ return;
+ }
+
+ ElfDbgPrint(( "[ELF] Run down context handle - 0x%lx\n", ElfHandle ));
+
+ if (ElfHandle->Signature != ELF_CONTEXTHANDLE_SIGN) {
+ ElfDbgPrint (("[ELF] Invalid context handle in rundown routine.\n"));
+ return;
+ }
+
+ pModule = FindModuleStrucFromAtom(ElfHandle->Atom);
+
+ //
+ // This shouldn't ever happen. It means that a handle got created,
+ // and it's module went away without the handle getting closed.
+ //
+
+ if (!pModule) {
+ ElfDbgPrint(("[ELF] - Could not find module for Atom %d on close\n",
+ ElfHandle->Atom));
+ return;
+ }
+
+ UnlinkContextHandle (ElfHandle); // Unlink it from the linked list
+
+ //
+ // If this handle was for a backup module, then we need to
+ // close the file and clean up the data structures. The standard logs
+ // (application, system and security) are never freed.
+ //
+
+ if (ElfHandle->Flags & ELF_LOG_HANDLE_BACKUP_LOG) {
+
+ Status = ElfpCloseLogFile (pModule->LogFile, ELF_LOG_CLOSE_BACKUP);
+
+ UnlinkLogModule(pModule);
+ ElfpFreeBuffer(pModule->ModuleName);
+
+ // What about DECREMENTING RefCount??? -Danl 2/9/94
+
+ if (pModule->LogFile->RefCount == 0) {
+ UnlinkLogFile(pModule->LogFile);
+ RtlDeleteResource ( &pModule->LogFile->Resource );
+ RtlDeleteSecurityObject(&pModule->LogFile->Sd);
+ ElfpFreeBuffer (pModule->LogFile->LogFileName);
+ ElfpFreeBuffer (pModule->LogFile);
+ }
+ ElfpFreeBuffer(pModule);
+
+ }
+ else {
+
+ //
+ // See if this handle had a ElfChangeNotify outstanding, and if so,
+ // remove it from the list. ElfChangeNotify can't be called with a
+ // handle to a backup file.
+
+ //
+ // Get exclusive access to the log file. This will ensure no one
+ // else is accessing the file.
+ //
+
+ RtlAcquireResourceExclusive (
+ &pModule->LogFile->Resource,
+ TRUE // Wait until available
+ );
+
+
+ //
+ // Walk the linked list and remove any entries for this handle
+ //
+
+ Notifiee = CONTAINING_RECORD (
+ pModule->LogFile->Notifiees.Flink,
+ struct _NOTIFIEE,
+ Next
+ );
+
+
+ while (Notifiee->Next.Flink != pModule->LogFile->Notifiees.Flink) {
+
+ //
+ // If it's for this handle, remove it from the list
+ //
+
+ if (Notifiee->Handle == ElfHandle) {
+
+ RemoveEntryList(&Notifiee->Next);
+ NtClose(Notifiee->Event);
+ NotifieeToDelete = Notifiee;
+
+ Notifiee = CONTAINING_RECORD(
+ Notifiee->Next.Flink,
+ struct _NOTIFIEE,
+ Next);
+
+ ElfpFreeBuffer(NotifieeToDelete);
+ }
+ else {
+
+ Notifiee = CONTAINING_RECORD (
+ Notifiee->Next.Flink,
+ struct _NOTIFIEE,
+ Next
+ );
+ }
+ }
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &pModule->LogFile->Resource );
+ }
+
+ ElfpFreeBuffer (ElfHandle); // Free the context-handle structure
+
+ return;
+}
diff --git a/private/eventlog/server/elfsec.c b/private/eventlog/server/elfsec.c
new file mode 100644
index 000000000..5d042cb38
--- /dev/null
+++ b/private/eventlog/server/elfsec.c
@@ -0,0 +1,920 @@
+
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ elfsec.c
+
+
+Author:
+
+ Dan Hinsley (danhi) 28-Mar-1992
+
+Environment:
+
+ Calls NT native APIs.
+
+Revision History:
+
+ 27-Oct-1993 danl
+ Make Eventlog service a DLL and attach it to services.exe.
+ Removed functions that create well-known SIDs. This information
+ is now passed into the Elfmain as a Global data structure containing
+ all well-known SIDs.
+ 28-Mar-1992 danhi
+ created - based on scsec.c in svcctrl by ritaw
+ 03-Mar-1995 markbl
+ Added guest & anonymous logon log access restriction feature.
+
+--*/
+
+#include <eventp.h>
+#include <elfcfg.h>
+
+#define PRIVILEGE_BUF_SIZE 512
+
+//-------------------------------------------------------------------//
+// //
+// Local function prototypes //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+ElfpGetPrivilege(
+ IN DWORD numPrivileges,
+ IN PULONG pulPrivileges
+ );
+
+DWORD
+ElfpReleasePrivilege(
+ VOID
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Structure that describes the mapping of generic access rights to //
+// object specific access rights for a LogFile object. //
+// //
+//-------------------------------------------------------------------//
+
+static GENERIC_MAPPING LogFileObjectMapping = {
+
+ STANDARD_RIGHTS_READ | // Generic read
+ ELF_LOGFILE_READ,
+
+ STANDARD_RIGHTS_WRITE | // Generic write
+ ELF_LOGFILE_WRITE,
+
+ STANDARD_RIGHTS_EXECUTE | // Generic execute
+ ELF_LOGFILE_START |
+ ELF_LOGFILE_STOP |
+ ELF_LOGFILE_CONFIGURE,
+
+ ELF_LOGFILE_ALL_ACCESS // Generic all
+ };
+
+
+//-------------------------------------------------------------------//
+// //
+// Functions //
+// //
+//-------------------------------------------------------------------//
+
+NTSTATUS
+ElfpCreateLogFileObject(
+ PLOGFILE LogFile,
+ DWORD Type,
+ ULONG GuestAccessRestriction
+ )
+/*++
+
+Routine Description:
+
+ This function creates the security descriptor which represents
+ an active log file.
+
+Arguments:
+
+ LogFile - pointer the the LOGFILE structure for this logfile
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD NumberOfAcesToUse;
+
+#define ELF_LOGFILE_OBJECT_ACES 10 // Number of ACEs in this DACL
+
+ RTL_ACE_DATA AceData[ELF_LOGFILE_OBJECT_ACES] = {
+
+ {ACCESS_DENIED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_ALL_ACCESS, &AnonymousLogonSid},
+
+ {ACCESS_DENIED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_ALL_ACCESS, &(ElfGlobalData->AliasGuestsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_ALL_ACCESS, &(ElfGlobalData->LocalSystemSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_READ | ELF_LOGFILE_CLEAR, &(ElfGlobalData->AliasAdminsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_BACKUP, &(ElfGlobalData->AliasBackupOpsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_READ | ELF_LOGFILE_CLEAR, &(ElfGlobalData->AliasSystemOpsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_READ, &(ElfGlobalData->WorldSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_WRITE, &(ElfGlobalData->AliasAdminsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_WRITE, &(ElfGlobalData->AliasSystemOpsSid)},
+
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0,
+ ELF_LOGFILE_WRITE, &(ElfGlobalData->WorldSid)}
+ };
+
+ PRTL_ACE_DATA pAceData = NULL;
+
+ //
+ // NON_SECURE logfiles let anyone read/write to them, secure ones
+ // only let admins/local system do this. so for secure files we just
+ // don't use the last ACE
+ //
+ // Adjust the ACL start based on the passed GuestAccessRestriction flag.
+ // The first two aces deny all log access to guests and/or anonymous
+ // logons. The flag, GuestAccessRestriction, indicates that these two
+ // deny access aces should be applied. Note that the deny aces and the
+ // GuestAccessRestriction flag are not applicable to the security log,
+ // since users and anonymous logons, by default, do not have access.
+ //
+
+ switch (Type) {
+
+ case ELF_LOGFILE_SECURITY:
+ pAceData = AceData + 2; // Deny ACEs *not* applicable
+ NumberOfAcesToUse = 3;
+ break;
+
+ case ELF_LOGFILE_SYSTEM:
+ if (GuestAccessRestriction == ELF_GUEST_ACCESS_RESTRICTED) {
+ pAceData = AceData; // Deny ACEs *applicable*
+ NumberOfAcesToUse = 8;
+ }
+ else {
+ pAceData = AceData + 2; // Deny ACEs *not* applicable
+ NumberOfAcesToUse = 6;
+ }
+ break;
+
+ case ELF_LOGFILE_APPLICATION:
+ if (GuestAccessRestriction == ELF_GUEST_ACCESS_RESTRICTED) {
+ pAceData = AceData; // Deny ACEs *applicable*
+ NumberOfAcesToUse = 10;
+ }
+ else {
+ pAceData = AceData + 2; // Deny ACEs *not* applicable
+ NumberOfAcesToUse = 8;
+ }
+ break;
+
+ }
+ Status = RtlCreateUserSecurityObject(
+ pAceData,
+ NumberOfAcesToUse,
+ NULL, // Owner
+ NULL, // Group
+ TRUE, // IsDirectoryObject
+ &LogFileObjectMapping,
+ &LogFile->Sd
+ );
+
+ if (! NT_SUCCESS(Status)) {
+ ElfDbgPrintNC((
+ "[ELF] ElfpCreateLogFileObject: ElfCreateUserSecurityObject "
+ "failed - %X\n", Status));
+ }
+
+ return (Status);
+}
+
+
+VOID
+ElfpDeleteLogFileObject(
+ PLOGFILE LogFile
+ )
+/*++
+
+Routine Description:
+
+ This function deletes the self-relative security descriptor which
+ represents an eventlog logfile object.
+
+Arguments:
+
+ LogFile - pointer the the LOGFILE structure for this logfile
+
+Return Value:
+
+ None.
+
+--*/
+{
+ (void) RtlDeleteSecurityObject(&LogFile->Sd);
+}
+
+
+NTSTATUS
+ElfpAccessCheckAndAudit(
+ IN LPWSTR SubsystemName,
+ IN LPWSTR ObjectTypeName,
+ IN LPWSTR ObjectName,
+ IN OUT IELF_HANDLE ContextHandle,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN ACCESS_MASK DesiredAccess,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN BOOL ForSecurityLog
+ )
+/*++
+
+Routine Description:
+
+ This function impersonates the caller so that it can perform access
+ validation using NtAccessCheckAndAuditAlarm; and reverts back to
+ itself before returning.
+
+Arguments:
+
+ SubsystemName - Supplies a name string identifying the subsystem
+ calling this routine.
+
+ ObjectTypeName - Supplies the name of the type of the object being
+ accessed.
+
+ ObjectName - Supplies the name of the object being accessed.
+
+ ContextHandle - Supplies the context handle to the object. On return, the
+ granted access is written to the AccessGranted field of this structure
+ if this call succeeds.
+
+ SecurityDescriptor - A pointer to the Security Descriptor against which
+ acccess is to be checked.
+
+ DesiredAccess - Supplies desired acccess mask. This mask must have been
+ previously mapped to contain no generic accesses.
+
+ GenericMapping - Supplies a pointer to the generic mapping associated
+ with this object type.
+
+ ForSecurityLog - TRUE if the access check is for the security log.
+ This is a special case that may require a privilege check.
+
+Return Value:
+
+ NT status mapped to Win32 errors.
+
+--*/
+{
+
+ NTSTATUS Status;
+ RPC_STATUS RpcStatus;
+
+ UNICODE_STRING Subsystem;
+ UNICODE_STRING ObjectType;
+ UNICODE_STRING Object;
+
+ BOOLEAN GenerateOnClose=FALSE;
+ NTSTATUS AccessStatus;
+ ACCESS_MASK GrantedAccess = 0;
+ HANDLE ClientToken = NULL;
+ PPRIVILEGE_SET pPrivilegeSet;
+ ULONG PrivilegeSetLength;
+ LUID luid = {0};
+ BOOL fResult=FALSE;
+ ULONG privileges[1];
+
+
+ GenericMapping = &LogFileObjectMapping;
+
+ PrivilegeSetLength = sizeof(PRIVILEGE_SET) + (sizeof(LUID_AND_ATTRIBUTES)*10);
+ pPrivilegeSet = (PPRIVILEGE_SET)LocalAlloc(LMEM_FIXED, PrivilegeSetLength);
+
+ if (pPrivilegeSet == NULL) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: LocalAlloc Failed %d\n",
+ GetLastError()));
+ return(GetLastError());
+ }
+
+ RtlInitUnicodeString(&Subsystem, SubsystemName);
+ RtlInitUnicodeString(&ObjectType, ObjectTypeName);
+ RtlInitUnicodeString(&Object, ObjectName);
+
+ if ((RpcStatus = RpcImpersonateClient(NULL)) != RPC_S_OK) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Failed to impersonate "
+ "client %08lx\n", RpcStatus));
+
+ LocalFree(pPrivilegeSet);
+ return RpcStatus;
+ }
+
+ //
+ // Get a token handle for the client
+ //
+ Status = NtOpenThreadToken (
+ NtCurrentThread(),
+ TOKEN_QUERY, // DesiredAccess
+ TRUE, // OpenAsSelf
+ &ClientToken);
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: NtOpenThreadToken Failed: "
+ "0x%lx\n",Status));
+
+ goto CleanExit;
+ }
+
+ //
+ // We want to see if we can get the desired access, and if we do
+ // then we also want all our other accesses granted.
+ // MAXIMUM_ALLOWED gives us this.
+ //
+ DesiredAccess |= MAXIMUM_ALLOWED;
+
+ Status = NtAccessCheck(
+ SecurityDescriptor,
+ ClientToken,
+ DesiredAccess,
+ GenericMapping,
+ pPrivilegeSet,
+ &PrivilegeSetLength,
+ &GrantedAccess,
+ &AccessStatus
+ );
+
+ if (! NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Error calling "
+ "NtAccessCheck %08lx\n", Status));
+ goto CleanExit;
+ }
+
+ if (AccessStatus != STATUS_SUCCESS) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Access status is %08lx\n",
+ AccessStatus));
+
+ //
+ // MarkBl 1/30/95 : Modified this code a bit to give backup operators
+ // the ability to open the security log for purposes
+ // of backup.
+ //
+
+ if ((AccessStatus == STATUS_ACCESS_DENIED) && (ForSecurityLog))
+ {
+ //
+ // MarkBl 1/30/95 : First, evalutate the existing code (performed
+ // for read or clear access), since its
+ // privilege check is more rigorous than mine.
+ //
+
+ Status = STATUS_ACCESS_DENIED;
+
+ if (!(DesiredAccess & ELF_LOGFILE_WRITE)) {
+
+ //
+ // If read or clear access to the security log is desired,
+ // then we will see if this user passes the privilege check.
+ //
+ //
+ // Do Privilege Check for SeSecurityPrivilege
+ // (SE_SECURITY_NAME).
+ //
+ // MarkBl 1/30/95 : Modified code to fall through on error
+ // instead of the jump to 'CleanExit'.
+ //
+
+ if (LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &luid)) {
+
+ pPrivilegeSet->PrivilegeCount = 1;
+ pPrivilegeSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
+ pPrivilegeSet->Privilege[0].Luid = luid;
+ pPrivilegeSet->Privilege[0].Attributes =
+ SE_PRIVILEGE_ENABLED;
+
+ if (PrivilegeCheck(
+ ClientToken,
+ pPrivilegeSet,
+ &fResult)) {
+ //
+ // If the privilege is enabled, then add READ and
+ // CLEAR to the granted access mask.
+ //
+ if (fResult) {
+ GrantedAccess |= (ELF_LOGFILE_READ |
+ ELF_LOGFILE_CLEAR);
+ Status = STATUS_SUCCESS;
+ }
+ }
+ else {
+ //
+ // Check Failed, Now what?
+ //
+ Status = GetLastError();
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: " \
+ "PrivilegeCheck failed. status is " \
+ "%d\n",Status));
+ }
+ }
+ else {
+ Status = GetLastError();
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: " \
+ "LookupPrivilegeValue failed. status " \
+ "is %d\n",Status));
+ }
+ }
+
+ //
+ // MarkBl 1/30/95 : Finally, my code. If this user has backup
+ // privilege, let the open succeed.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &luid)) {
+
+ pPrivilegeSet->PrivilegeCount = 1;
+ pPrivilegeSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
+ pPrivilegeSet->Privilege[0].Luid = luid;
+ pPrivilegeSet->Privilege[0].Attributes =
+ SE_PRIVILEGE_ENABLED;
+
+ if (PrivilegeCheck(
+ ClientToken,
+ pPrivilegeSet,
+ &fResult)) {
+ if (fResult) {
+ GrantedAccess |= ELF_LOGFILE_BACKUP;
+ Status = STATUS_SUCCESS;
+ }
+ }
+ else {
+ Status = GetLastError();
+ goto CleanExit;
+ }
+ }
+ else {
+ Status = GetLastError();
+ goto CleanExit;
+ }
+ }
+ }
+ else {
+ Status = AccessStatus;
+ }
+ }
+
+
+ //
+ // Revert to Self
+ //
+ if ((RpcStatus = RpcRevertToSelf()) != RPC_S_OK) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Fail to revert to "
+ "self %08lx\n", RpcStatus));
+ //
+ // We don't return the error status here because we don't want
+ // to write over the other status that is being returned.
+ //
+
+ }
+
+ //
+ // Get SeAuditPrivilege so I can call NtOpenObjectAuditAlarm.
+ // If any of this stuff fails, I don't want the status to overwrite the
+ // status that I got back from the access and privilege checks.
+ //
+
+ privileges[0] = SE_AUDIT_PRIVILEGE;
+ AccessStatus = ElfpGetPrivilege( 1, privileges);
+
+ if (!NT_SUCCESS(AccessStatus)) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: ElfpGetPrivilege "
+ "(Enable) failed. Status is 0x%lx\n",AccessStatus));
+ }
+
+ //
+ // Call the Audit Alarm function.
+ //
+ GenerateOnClose = FALSE;
+ AccessStatus = NtOpenObjectAuditAlarm (
+ &Subsystem,
+ (PVOID)ContextHandle,
+ &ObjectType,
+ &Object,
+ SecurityDescriptor,
+ ClientToken, // Handle ClientToken
+ DesiredAccess,
+ GrantedAccess,
+ pPrivilegeSet, // PPRIVLEGE_SET
+ FALSE, // BOOLEAN ObjectCreation,
+ TRUE, // BOOLEAN AccessGranted,
+ &GenerateOnClose
+ );
+
+ if (!NT_SUCCESS(AccessStatus)) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: NtOpenObjectAuditAlarm "
+ "failed. status is 0x%lx\n",AccessStatus));
+ }
+ else {
+ if (GenerateOnClose) {
+ ContextHandle->Flags |= ELF_LOG_HANDLE_GENERATE_ON_CLOSE;
+ }
+ }
+
+ //
+ // Update the GrantedAccess in the context handle.
+ //
+ ContextHandle->GrantedAccess = GrantedAccess;
+
+ NtClose(ClientToken);
+
+ ElfpReleasePrivilege();
+
+ LocalFree(pPrivilegeSet);
+
+ return(Status);
+
+CleanExit:
+ //
+ // Revert to Self
+ //
+ if ((RpcStatus = RpcRevertToSelf()) != RPC_S_OK) {
+ ElfDbgPrint(("[ELF] ElfpAccessCheckAndAudit: Fail to revert to "
+ "self %08lx\n", RpcStatus));
+ //
+ // We don't return the error status here because we don't want
+ // to write over the other status that is being returned.
+ //
+
+ }
+ if (ClientToken != NULL) {
+ NtClose(ClientToken);
+ }
+
+ LocalFree(pPrivilegeSet);
+
+ return(Status);
+}
+
+VOID
+ElfpCloseAudit(
+ IN LPWSTR SubsystemName,
+ IN IELF_HANDLE ContextHandle
+ )
+
+/*++
+
+Routine Description:
+
+ If the GenerateOnClose flag in the ContextHandle is set, then this function
+ calls NtCloseAuditAlarm in order to generate a close audit for this handle.
+
+Arguments:
+
+ ContextHandle - This is a pointer to an ELF_HANDLE structure. This is the
+ handle that is being closed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UNICODE_STRING Subsystem;
+ NTSTATUS Status;
+ NTSTATUS AccessStatus;
+ ULONG privileges[1];
+
+ RtlInitUnicodeString(&Subsystem, SubsystemName);
+
+ if (ContextHandle->Flags & ELF_LOG_HANDLE_GENERATE_ON_CLOSE) {
+ BOOLEAN WasEnabled = FALSE;
+
+ //
+ // Get Audit Privilege
+ //
+ privileges[0] = SE_AUDIT_PRIVILEGE;
+ AccessStatus = ElfpGetPrivilege( 1, privileges);
+
+ if (!NT_SUCCESS(AccessStatus)) {
+ ElfDbgPrint(("[ELF] ElfpCloseAudit: ElfpGetPrivilege "
+ "(Enable) failed. Status is 0x%lx\n",AccessStatus));
+ }
+
+ //
+ // Generate the Audit.
+ //
+ Status = NtCloseObjectAuditAlarm(
+ &Subsystem,
+ ContextHandle,
+ TRUE);
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] ElfpCloseAudit: NtCloseObjectAuditAlarm Failed: "
+ "0x%lx\n",Status));
+ }
+
+ ContextHandle->Flags &= (~ELF_LOG_HANDLE_GENERATE_ON_CLOSE);
+
+ ElfpReleasePrivilege();
+
+#ifdef REMOVE
+ //
+ // Release Audit Privilege
+ //
+ Status = RtlAdjustPrivilege(
+ SE_AUDIT_PRIVILEGE,
+ FALSE, // Disable
+ FALSE, // Use Process's token
+ &WasEnabled);
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] ElfpCloseAudit: RtlAdjustPrivilege "
+ "(Disable) failed. Status is 0x%lx\n",Status));
+ }
+#endif
+
+ }
+ return;
+}
+DWORD
+ElfpGetPrivilege(
+ IN DWORD numPrivileges,
+ IN PULONG pulPrivileges
+ )
+/*++
+
+Routine Description:
+
+ This function alters the privilege level for the current thread.
+
+ It does this by duplicating the token for the current thread, and then
+ applying the new privileges to that new token, then the current thread
+ impersonates with that new token.
+
+ Privileges can be relinquished by calling ElfpReleasePrivilege().
+
+Arguments:
+
+ numPrivileges - This is a count of the number of privileges in the
+ array of privileges.
+
+ pulPrivileges - This is a pointer to the array of privileges that are
+ desired. This is an array of ULONGs.
+
+Return Value:
+
+ NO_ERROR - If the operation was completely successful.
+
+ Otherwise, it returns mapped return codes from the various NT
+ functions that are called.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntStatus;
+ HANDLE ourToken;
+ HANDLE newToken;
+ OBJECT_ATTRIBUTES Obja;
+ SECURITY_QUALITY_OF_SERVICE SecurityQofS;
+ ULONG bufLen;
+ ULONG returnLen;
+ PTOKEN_PRIVILEGES pPreviousState;
+ PTOKEN_PRIVILEGES pTokenPrivilege = NULL;
+ DWORD i;
+
+ //
+ // Initialize the Privileges Structure
+ //
+ pTokenPrivilege = (PTOKEN_PRIVILEGES) LocalAlloc(
+ LMEM_FIXED,
+ sizeof(TOKEN_PRIVILEGES) +
+ (sizeof(LUID_AND_ATTRIBUTES) *
+ numPrivileges)
+ );
+
+ if (pTokenPrivilege == NULL) {
+ status = GetLastError();
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege:LocalAlloc Failed %d\n", status));
+ return(status);
+ }
+ pTokenPrivilege->PrivilegeCount = numPrivileges;
+ for (i=0; i<numPrivileges ;i++ ) {
+ pTokenPrivilege->Privileges[i].Luid = RtlConvertLongToLuid(
+ pulPrivileges[i]);
+ pTokenPrivilege->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED;
+
+ }
+
+ //
+ // Initialize Object Attribute Structure.
+ //
+ InitializeObjectAttributes(&Obja,NULL,0L,NULL,NULL);
+
+ //
+ // Initialize Security Quality Of Service Structure
+ //
+ SecurityQofS.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ SecurityQofS.ImpersonationLevel = SecurityImpersonation;
+ SecurityQofS.ContextTrackingMode = FALSE; // Snapshot client context
+ SecurityQofS.EffectiveOnly = FALSE;
+
+ Obja.SecurityQualityOfService = &SecurityQofS;
+
+ //
+ // Allocate storage for the structure that will hold the Previous State
+ // information.
+ //
+ pPreviousState = (PTOKEN_PRIVILEGES) LocalAlloc(
+ LMEM_FIXED,
+ PRIVILEGE_BUF_SIZE
+ );
+ if (pPreviousState == NULL) {
+
+ status = GetLastError();
+
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege: LocalAlloc Failed %d\n",
+ status));
+
+ LocalFree(pTokenPrivilege);
+ return(status);
+
+ }
+
+ //
+ // Open our own Token
+ //
+ ntStatus = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_DUPLICATE,
+ &ourToken);
+
+ if (!NT_SUCCESS(ntStatus)) {
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtOpenThreadToken Failed "
+ "0x%lx" "\n", ntStatus));
+
+ LocalFree(pPreviousState);
+ LocalFree(pTokenPrivilege);
+ return(RtlNtStatusToDosError(ntStatus));
+ }
+
+ //
+ // Duplicate that Token
+ //
+ ntStatus = NtDuplicateToken(
+ ourToken,
+ TOKEN_IMPERSONATE | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
+ &Obja,
+ FALSE, // Duplicate the entire token
+ TokenImpersonation, // TokenType
+ &newToken); // Duplicate token
+
+ if (!NT_SUCCESS(ntStatus)) {
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtDuplicateToken Failed "
+ "0x%lx" "\n", ntStatus));
+
+ LocalFree(pPreviousState);
+ LocalFree(pTokenPrivilege);
+ NtClose(ourToken);
+ return(RtlNtStatusToDosError(ntStatus));
+ }
+
+ //
+ // Add new privileges
+ //
+ bufLen = PRIVILEGE_BUF_SIZE;
+ ntStatus = NtAdjustPrivilegesToken(
+ newToken, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ pTokenPrivilege, // NewState
+ bufLen, // bufferSize for previous state
+ pPreviousState, // pointer to previous state info
+ &returnLen); // numBytes required for buffer.
+
+ if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
+
+ LocalFree(pPreviousState);
+
+ bufLen = returnLen;
+
+ pPreviousState = (PTOKEN_PRIVILEGES) LocalAlloc(
+ LMEM_FIXED,
+ (UINT) bufLen
+ );
+
+ ntStatus = NtAdjustPrivilegesToken(
+ newToken, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ pTokenPrivilege, // NewState
+ bufLen, // bufferSize for previous state
+ pPreviousState, // pointer to previous state info
+ &returnLen); // numBytes required for buffer.
+
+ }
+ if (!NT_SUCCESS(ntStatus)) {
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtAdjustPrivilegesToken Failed "
+ "0x%lx" "\n", ntStatus));
+
+ LocalFree(pPreviousState);
+ LocalFree(pTokenPrivilege);
+ NtClose(ourToken);
+ NtClose(newToken);
+ return(RtlNtStatusToDosError(ntStatus));
+ }
+
+ //
+ // Begin impersonating with the new token
+ //
+ ntStatus = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID)&newToken,
+ (ULONG)sizeof(HANDLE));
+
+ if (!NT_SUCCESS(ntStatus)) {
+ ElfDbgPrint(("[ELF] ElfpGetPrivilege: NtAdjustPrivilegesToken Failed "
+ "0x%lx" "\n", ntStatus));
+
+ LocalFree(pPreviousState);
+ LocalFree(pTokenPrivilege);
+ NtClose(ourToken);
+ NtClose(newToken);
+ return(RtlNtStatusToDosError(ntStatus));
+ }
+
+ //
+ // BUGBUG: Do I need to keep some of this around to pass to the
+ // ReleasePrivilege function?
+ //
+ LocalFree(pPreviousState);
+ LocalFree(pTokenPrivilege);
+ NtClose(ourToken);
+ NtClose(newToken);
+
+ return(NO_ERROR);
+}
+
+DWORD
+ElfpReleasePrivilege(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function relinquishes privileges obtained by calling ElfpGetPrivilege().
+
+Arguments:
+
+ none
+
+Return Value:
+
+ NO_ERROR - If the operation was completely successful.
+
+ Otherwise, it returns mapped return codes from the various NT
+ functions that are called.
+
+
+--*/
+{
+ NTSTATUS ntStatus;
+ HANDLE NewToken;
+
+
+ //
+ // BUGBUG: Do I need to Adjust the Privileges back to what they
+ // were first? (if so, I need somemore info passed to this fcn)
+ //
+
+ //
+ // Revert To Self.
+ //
+ NewToken = NULL;
+
+ ntStatus = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE));
+
+ if ( !NT_SUCCESS(ntStatus) ) {
+ return(RtlNtStatusToDosError(ntStatus));
+ }
+
+
+ return(NO_ERROR);
+}
diff --git a/private/eventlog/server/elfutil.c b/private/eventlog/server/elfutil.c
new file mode 100644
index 000000000..6d46081c6
--- /dev/null
+++ b/private/eventlog/server/elfutil.c
@@ -0,0 +1,1737 @@
+/*++
+
+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;
+
+ ElfDbgPrint(("[ELF] GetModuleStruc: "));
+
+ Status = RtlUnicodeStringToAnsiString (
+ &ModuleNameA,
+ ModuleName,
+ TRUE
+ );
+
+ ASSERT (NT_SUCCESS(Status)); // This MUST NOT fail.
+
+ //
+ // 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);
+
+ } else {
+
+ ElfDbgPrint((" Atom = %d \n", ModuleAtom));
+
+ return (FindModuleStrucFromAtom( ModuleAtom ));
+
+ }
+
+}
+
+
+
+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 = LoadLibraryW(L"NETMSG.DLL");
+
+ //
+ // 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,
+ (LPVOID) 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;
+ static ULONG LastPulseTime = 0;
+ 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);
+ RtlTimeToSecondsSince1970(&Time, &CurrentTime);
+
+ if (NT_SUCCESS(Status)) {
+ CurrentTime = CurrentTime - MINIMUM_PULSE_TIME;
+ if (CurrentTime > LastPulseTime) {
+
+ //
+ // Remember that we pulsed
+ //
+
+ LastPulseTime = 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) {
+ if (!SendAdminAlert(QueuedEvent->Event.Alert.MessageId,
+ QueuedEvent->Event.Alert.NumberOfStrings,
+ (PUNICODE_STRING)((PBYTE) QueuedEvent +
+ FIELD_OFFSET(ELF_QUEUED_EVENT, Event) +
+ sizeof(ELF_ALERT_RECORD)))) {
+
+ LastAlertFailed = TRUE;
+ LastAlertTried = CurrentTime;
+
+ }
+ else {
+ LastAlertFailed = FALSE;
+ 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 (
+ )
+
+/*++
+
+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()
+
+/*++
+
+Routine Description:
+
+ This routine releases the global resource.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ RtlReleaseResource ( &GlobalElfResource );
+}
+
+
+
+NTSTATUS
+ElfImpersonateClient(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function calls RpcImpersonateClient to impersonate the current
+ caller of an API.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS (which is the same as RPC_STATUS)
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+
+
+ if ((RpcStatus = RpcImpersonateClient(NULL)) != RPC_S_OK) {
+ ElfDbgPrintNC(("[ELF] Fail to impersonate client %lx\n", RpcStatus));
+ }
+
+ return (I_RpcMapWin32Status(RpcStatus));
+}
+
+
+
+NTSTATUS
+ElfRevertToSelf(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function calls RpcRevertToSelf to undo an impersonation.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS (which is the same as RPC_STATUS)
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+
+
+ if ((RpcStatus = RpcRevertToSelf()) != RPC_S_OK) {
+ ElfDbgPrintNC(("[ELF] Fail to revert to self %lx\n", RpcStatus));
+ ASSERT(FALSE);
+ }
+
+ return (RpcStatus);
+}
+
+
+
+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);
+
+ if (pLogFile->FileList.Flink == LogFilesHead.Flink) {
+ return(NULL);
+ }
+ else {
+ return (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;
+ 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.
+ //
+
+ StringsSize = 0;
+ 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 =
+ (PVOID) 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) {
+ 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);
+ RtlMoveMemory(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);
+}
diff --git a/private/eventlog/server/eventlog.c b/private/eventlog/server/eventlog.c
new file mode 100644
index 000000000..e2a4b1058
--- /dev/null
+++ b/private/eventlog/server/eventlog.c
@@ -0,0 +1,1510 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ EVENTLOG.C
+
+Abstract:
+
+ This file contains the main routines for the NT OS/2 Event
+ Logging Service.
+
+Author:
+
+ Rajen Shah (rajens) 1-Jul-1991
+
+[Environment:]
+
+ User Mode - Win32, except for NTSTATUS returned by some functions.
+
+Revision History:
+
+ 26-Jan-1994 Danl
+ SetUpModules: Fixed memory leak where the buffers for the enumerated
+ key names were never free'd. Also fixed problem where the size of
+ the MULTI_SZ buffer used for the "Sources" key was calculated by
+ using the names in the registry, while the copying was done
+ using the names in the module list. When registry keys are deleted,
+ the module list entry is retained until the next boot. Since the
+ module list is larger, it would overwrite the MULTI_SZ buffer.
+ 1-Nov-1993 Danl
+ Make Eventlog service a DLL and attach it to services.exe.
+ Pass in GlobalData to Elfmain. This GlobalData structure contains
+ all well-known SIDs and pointers to the Rpc Server (Start & Stop)
+ routines. Get rid of the service process main function.
+ 1-Jul-1991 RajenS
+ created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <ntrpcp.h>
+#include <elfcfg.h>
+#include <string.h>
+#include <stdlib.h> // getenv()
+#include <tstr.h> // WCSSIZE
+#include <alertmsg.h> // ALERT_ELF manifests
+#include <stdio.h> // printf
+
+#ifdef _CAIRO_
+#include <elfextrn.h>
+#endif // _CAIRO_
+
+//
+// Bit Flags used for Progress Reporting in SetupDataStruct().
+//
+#define LOGFILE_OPENED 0x00000001
+#define MODULE_LINKED 0x00000002
+#define LOGFILE_LINKED 0x00000004
+
+//
+// Local Function Prorotypes
+//
+VOID
+ElfInitMessageBoxTitle(
+ VOID
+ );
+
+
+NTSTATUS
+ElfStartRPCServer ()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status;
+
+
+ ElfDbgPrint(("[ELF] Starting RPC server.\n"));
+
+ Status = RpcpAddInterface (
+ wname_Eventlogsvc,
+ eventlog_ServerIfHandle
+ );
+
+ if (Status != NERR_Success) {
+ ElfDbgPrintNC(("[ELF] RpcpAddInterface = %u\n", Status ));
+
+ }
+ return (I_RpcMapWin32Status(Status)); // Return status to caller
+
+}
+
+
+NTSTATUS
+SetUpDataStruct (
+ PUNICODE_STRING LogFileName,
+ ULONG MaxFileSize,
+ ULONG Retention,
+ ULONG GuestAccessRestriction,
+ PUNICODE_STRING ModuleName,
+ HANDLE hLogFile,
+ ELF_LOG_TYPE LogType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets up the information for one module. It is called from
+ ElfSetUpConfigDataStructs for each module to be configured.
+
+ Module information is passed into this routine and a LOGMODULE structure
+ is created for it. If the logfile associated with this module doesn't
+ exist, a LOGFILE structure is created for it, and added to the linked
+ list of LOGFILE structures. The LOGMODULE is associated with the LOGFILE,
+ and it is added to the linked list of LOGMODULE structures. The logfile
+ is opened and mapped to memory.
+
+ Finally, at the end, this function calls SetUpModules, which looks at
+ all the subkeys in the registry under this logfile, and adds any new ones
+ to the linked list, and updates the Sources MULTI_SZ for the event viewer.
+
+Arguments:
+
+ LogFileName - Name of log file for this module. If this routine needs
+ a copy of this name it will make one, so that the caller can free
+ the name afterwards if that is desired.
+
+ MaxFileSize - Max size of the log file.
+ Retention - Max retention for the file.
+ ModuleName - Name of module that this file is associated with.
+ RegistryHandle - Handle to the root node for this LogFile's info
+ in the registry. This is used to enumerate all the
+ modules under this key.
+
+Return Value:
+
+ Pointer to Module structure that is allocated in this routine.
+ NTSTATUS
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLOGFILE pLogFile=NULL;
+ PLOGMODULE pModule=NULL;
+ ANSI_STRING ModuleNameA;
+ DWORD Type;
+ BOOL bLogFileAllocatedHere=FALSE;
+ PUNICODE_STRING SavedBackupFileName=NULL;
+ DWORD StringLength;
+ PLOGMODULE OldDefaultLogModule=NULL;
+ DWORD Progress = 0L;
+
+ //
+ // Argument check.
+ //
+
+ if ((LogFileName == NULL) ||
+ (LogFileName->Buffer == NULL) ||
+ (ModuleName == NULL))
+ {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ // If the default log file for a module is also being used by another
+ // module, then we just link that same file structure with the other
+ // module.
+ //
+ // Truncate the maximum size of the log file to a 4K boundary.
+ // This is to allow for page granularity.
+ //
+
+ pLogFile = FindLogFileFromName (LogFileName);
+
+ pModule = ElfpAllocateBuffer (sizeof (LOGMODULE) );
+ if (pModule == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ if (pLogFile == NULL) {
+
+ //--------------------------------------
+ // CREATE A NEW LOGFILE !!
+ //--------------------------------------
+ // A logfile by this name doesn't exist yet. So we will create
+ // one so that we can add the module to it.
+ //
+
+ bLogFileAllocatedHere = TRUE;
+
+ pLogFile = ElfpAllocateBuffer (sizeof (LOGFILE) );
+
+ if (pLogFile == NULL) {
+ ElfpFreeBuffer (pModule);
+ return(STATUS_NO_MEMORY);
+ }
+
+ ElfDbgPrint(("[ELF] Set up file data\n"));
+
+ //
+ // Allocate a new LogFileName that can be attached to the
+ // new pLogFile structure.
+ //
+ StringLength = LogFileName->Length + sizeof(WCHAR);
+ SavedBackupFileName = (PUNICODE_STRING) ElfpAllocateBuffer(
+ sizeof(UNICODE_STRING) + StringLength);
+
+ if (SavedBackupFileName == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto ErrorExit;
+ }
+
+ SavedBackupFileName->Buffer = (LPWSTR)((LPBYTE) SavedBackupFileName +
+ sizeof(UNICODE_STRING));
+
+ SavedBackupFileName->Length = LogFileName->Length;
+ SavedBackupFileName->MaximumLength = (USHORT) StringLength;
+ RtlMoveMemory(SavedBackupFileName->Buffer, LogFileName->Buffer,
+ LogFileName->Length);
+ SavedBackupFileName->Buffer[SavedBackupFileName->Length / sizeof(WCHAR)] =
+ L'\0';
+
+ //
+ // This is the first user - RefCount gets incrememted below
+ //
+ pLogFile->RefCount = 0;
+ pLogFile->FileHandle = NULL;
+ pLogFile->LogFileName = SavedBackupFileName;
+ pLogFile->ConfigMaxFileSize = ELFFILESIZE(MaxFileSize);
+ pLogFile->Retention = Retention;
+
+
+ //
+ // Save away the default module name for this file
+ //
+ pLogFile->LogModuleName = ElfpAllocateBuffer(
+ sizeof(UNICODE_STRING) + ModuleName->MaximumLength);
+
+ if (pLogFile->LogModuleName == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto ErrorExit;
+ }
+
+ pLogFile->LogModuleName->MaximumLength = ModuleName->MaximumLength;
+ pLogFile->LogModuleName->Buffer =
+ (LPWSTR)(pLogFile->LogModuleName + 1);
+ RtlCopyUnicodeString(pLogFile->LogModuleName, ModuleName);
+
+ InitializeListHead (&pLogFile->Notifiees);
+
+
+ pLogFile->NextClearMaxFileSize = pLogFile->ConfigMaxFileSize;
+
+ RtlInitializeResource ( &pLogFile->Resource );
+ LinkLogFile ( pLogFile ); // Link it in
+
+ Progress |= LOGFILE_LINKED;
+
+ } // endif (pLogfile == NULL)
+
+ //--------------------------------------
+ // ADD THE MODULE TO THE LOG MODULE LIST
+ //--------------------------------------
+ // Set up the module data structure for the default (which is
+ // the same as the logfile keyname).
+ //
+
+ pLogFile->RefCount++;
+ pModule->LogFile = pLogFile;
+ pModule->ModuleName = (LPWSTR) ModuleName->Buffer;
+
+ Status = RtlUnicodeStringToAnsiString (
+ &ModuleNameA,
+ ModuleName,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ pLogFile->RefCount--;
+ goto ErrorExit;
+ }
+
+ //
+ // Link the new module in.
+ //
+
+ LinkLogModule(pModule, &ModuleNameA);
+
+ RtlFreeAnsiString (&ModuleNameA);
+
+ Progress |= MODULE_LINKED;
+
+ //
+ // Open up the file and map it to memory. Impersonate the
+ // caller so we can use UNC names
+ //
+
+ if (LogType == ElfBackupLog) ElfImpersonateClient();
+
+ Status = ElfOpenLogFile (pLogFile, LogType);
+
+ if (LogType == ElfBackupLog) ElfRevertToSelf();
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF] Couldn't open %ws for module %ws\n",
+ LogFileName->Buffer, ModuleName->Buffer));
+
+ if (LogType != ElfBackupLog) {
+ ElfpCreateQueuedAlert(ALERT_ELF_LogFileNotOpened, 1,
+ &(ModuleName->Buffer));
+ }
+ pLogFile->RefCount--;
+ goto ErrorExit;
+ }
+
+ Progress |= LOGFILE_OPENED;
+ //
+ // If this is the application module, remember the pointer
+ // to use if a module doesn't have an entry in the registry
+ //
+
+ if (!_wcsicmp(ModuleName->Buffer, ELF_DEFAULT_MODULE_NAME)) {
+ OldDefaultLogModule = ElfDefaultLogModule;
+ ElfDefaultLogModule = pModule;
+ }
+
+ //
+ // Create the security descriptor for this logfile. Only
+ // the system and security modules are secured against
+ // reads and writes by world.
+ //
+
+ if (!_wcsicmp(ModuleName->Buffer, ELF_SYSTEM_MODULE_NAME)) {
+ Type = ELF_LOGFILE_SYSTEM;
+ }
+ else if (!_wcsicmp(ModuleName->Buffer, ELF_SECURITY_MODULE_NAME)) {
+ Type = ELF_LOGFILE_SECURITY;
+ }
+ else {
+ Type = ELF_LOGFILE_APPLICATION;
+ }
+
+ //
+ // Create a Security Descriptor for this Logfile
+ // (RtlDeleteSecurityObject() can be used to free
+ // pLogFile->Sd).
+ //
+ Status = ElfpCreateLogFileObject(pLogFile, Type, GuestAccessRestriction);
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrintNC(("[ELF] Could not create the security "
+ "descriptor for logfile %ws\n", ModuleName->Buffer));
+
+ pLogFile->RefCount--;
+ goto ErrorExit;
+ }
+
+ //
+ // Now that we've added the default module name, see if there are any
+ // modules configured to log to this file, and if so, create the module
+ // structures for them.
+ //
+
+ SetUpModules(hLogFile, pLogFile, FALSE);
+
+ return (STATUS_SUCCESS);
+
+ErrorExit:
+
+ if (Progress & LOGFILE_OPENED) {
+ ElfpCloseLogFile(pLogFile, ELF_LOG_CLOSE_BACKUP);
+ }
+
+ if (Progress & MODULE_LINKED) {
+ UnlinkLogModule(pModule);
+ DeleteAtom(pModule->ModuleAtom);
+ }
+
+ if (bLogFileAllocatedHere) {
+
+ if (Progress & LOGFILE_LINKED) {
+ UnlinkLogFile(pLogFile);
+ RtlDeleteResource (&pLogFile->Resource);
+ }
+ if (pLogFile->LogModuleName != NULL) {
+ ElfpFreeBuffer(pLogFile->LogModuleName);
+ }
+ if (SavedBackupFileName != NULL) {
+ ElfpFreeBuffer(SavedBackupFileName);
+ }
+ if (pLogFile != NULL) {
+ ElfpFreeBuffer(pLogFile);
+ }
+ }
+
+ ElfpFreeBuffer(pModule);
+
+ if (OldDefaultLogModule != NULL) {
+ ElfDefaultLogModule = OldDefaultLogModule;
+ }
+ return(Status);
+}
+
+NTSTATUS
+SetUpModules (
+ HANDLE hLogFile,
+ PLOGFILE pLogFile,
+ BOOLEAN bAllowDupes
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets up the information for all modules for a logfile.
+
+ The subkeys under a logfile in the eventlog portion of the registry
+ are enumerated. For each unique subkey, a LOGMODULE structure is
+ created. Each new structures is added to a linked list
+ of modules for that logfile.
+
+ If there was one or more unique subkeys, meaning the list has changed
+ since we last looked, then we go through the entire linked list of
+ log modules, and create a MULTI_SZ list of all the modules. This list
+ is stored in the Sources value for that logfile for the event viewer
+ to use.
+
+ BUGBUG:
+ NOTE: A module is never un-linked from the linked list of log modules
+ even if the registry subkey for it is removed. This should probably
+ be done sometime. It would make the eventlog more robust.
+
+Arguments:
+
+ hLogFile - Registry key for the Log File node
+ pLogFile - pointer to the log file structure
+ bAllowDupes - If true, it's ok to already have a module with the same
+ name (used when processing change notify of registry)
+
+Return Value:
+
+ NTSTATUS - If unsuccessful, it is not a fatal error.
+
+ Even if this status is unsuccessful, me may have been able
+ to store some of the new subkeys in the LogModule list. Also, we
+ may have been able to update the Sources MULTI_SZ list.
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
+ PKEY_NODE_INFORMATION KeyBuffer = (PKEY_NODE_INFORMATION) Buffer;
+ ULONG ActualSize;
+ PWCHAR SubKeyString;
+ UNICODE_STRING NewModule;
+ ANSI_STRING ModuleNameA;
+ PLOGMODULE pModule;
+ ULONG Index = 0;
+ ATOM Atom;
+ PWCHAR pList;
+ DWORD ListLength = 0;
+ UNICODE_STRING ListName;
+ BOOLEAN ListChanged = FALSE;
+ PLIST_ENTRY pListEntry;
+#ifdef _CAIRO_
+ SHORT sCategory;
+ SHORT sSeverity;
+#endif // _CAIRO_
+
+ //
+ // Create the module structures for all modules under this logfile. We
+ // don't actually need to open the key, since we don't use any information
+ // stored there, it's existence is all we care about here. Any data is
+ // used by the Event Viewer (or any viewing app). If this is used to
+ // setup a backup file, hLogFile is NULL since there aren't any other
+ // modules to map to this file.
+ //
+
+ while (NT_SUCCESS(Status) && hLogFile) {
+
+ Status = NtEnumerateKey(hLogFile, Index++, KeyNodeInformation,
+ KeyBuffer, ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // It turns out the Name isn't null terminated, so we need
+ // to copy it somewhere and null terminate it before we use it
+ //
+
+ SubKeyString = ElfpAllocateBuffer(KeyBuffer->NameLength +
+ sizeof(WCHAR));
+ if (!SubKeyString) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ memcpy(SubKeyString, KeyBuffer->Name, KeyBuffer->NameLength);
+ SubKeyString[KeyBuffer->NameLength / sizeof(WCHAR)] = L'\0' ;
+
+ //
+ // Add the atom for this module name
+ //
+
+ RtlInitUnicodeString(&NewModule, SubKeyString);
+
+ Status = RtlUnicodeStringToAnsiString (
+ &ModuleNameA,
+ &NewModule,
+ TRUE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ //
+ // We can't continue, so we will leave the modules
+ // we've linked so far, and move on in an attempt to
+ // create the Sources MULTI_SZ list.
+ //
+ ElfpFreeBuffer(SubKeyString);
+ break;
+ }
+
+ Atom = FindAtomA (ModuleNameA.Buffer);
+
+ //
+ // Make sure we've not already added one by this name
+ //
+
+#ifdef _CAIRO_
+ if (pModule = FindModuleStrucFromAtom(Atom)) {
+#else
+ if (FindModuleStrucFromAtom(Atom)) {
+#endif // _CAIRO_
+
+ //
+ // We've already encountered a module by this name. If
+ // this is init time, it's a configuration error. Report
+ // it and move on. If we're processing a change notify
+ // from the registry, this is ok, so just press on
+ //
+ // ** NEW FOR CAIRO **
+ //
+ // Update the module alert category & severity values. i.e.,
+ // only upon registry change notify.
+ //
+
+ if (!bAllowDupes) {
+
+ ElfDbgPrint(("[ELF] Same module exists in two log files - "
+ "%ws\n", SubKeyString));
+ }
+
+#ifdef _CAIRO_
+ if (GetSourceAlertFilterFromRegistry(hLogFile,
+ &NewModule,
+ &sCategory,
+ &sSeverity))
+ {
+ pModule->AlertCategory = sCategory;
+ pModule->AlertSeverity = sSeverity;
+ }
+#endif // _CAIRO_
+
+ RtlFreeAnsiString (&ModuleNameA);
+ ElfpFreeBuffer(SubKeyString);
+ continue;
+
+ }
+
+ ListChanged = TRUE;
+
+ pModule = ElfpAllocateBuffer (sizeof (LOGMODULE) );
+ if (!pModule) {
+ ElfpFreeBuffer(SubKeyString);
+ return(STATUS_NO_MEMORY);
+ }
+
+ //
+ // Set up a module data structure for this module
+ //
+
+ pModule->LogFile = pLogFile;
+ pModule->ModuleName = SubKeyString;
+
+#ifdef _CAIRO_
+ if (GetSourceAlertFilterFromRegistry(hLogFile,
+ &NewModule,
+ &sCategory,
+ &sSeverity))
+ {
+ pModule->AlertCategory = sCategory;
+ pModule->AlertSeverity = sSeverity;
+ }
+ else
+ {
+ pModule->AlertCategory = pModule->AlertSeverity = 0;
+ }
+#endif // _CAIRO_
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Link the new module in.
+ //
+
+ LinkLogModule(pModule, &ModuleNameA);
+
+ RtlFreeAnsiString (&ModuleNameA);
+ }
+ }
+ }
+
+ if (Status == STATUS_NO_MORE_ENTRIES) {
+
+ //
+ // It's not required that there are configured modules for a log
+ // file.
+ //
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // If the list has changed, or if we've been called during init, and not
+ // as the result of a changenotify on the registry (bAllowDupes == FALSE)
+ // then create the sources key
+ //
+
+ if (hLogFile && (ListChanged || !bAllowDupes)) {
+
+ //
+ // Now create a MULTI_SZ entry with all the module names for eventvwr
+ //
+ // STEP 1: Calculate amount of storage needed by running thru the
+ // module list, finding any module that uses this log file.
+ //
+ pListEntry = LogModuleHead.Flink;
+ while (pListEntry != &LogModuleHead) {
+
+ pModule = CONTAINING_RECORD (pListEntry, LOGMODULE, ModuleList);
+
+ if (pModule->LogFile == pLogFile) {
+ //
+ // This one is for the log we're working on, get the
+ // size of it's name.
+ //
+ ListLength += WCSSIZE(pModule->ModuleName);
+ }
+ pListEntry = pModule->ModuleList.Flink;
+ }
+
+ //
+ // STEP 2: Allocate storage for the MULTI_SZ.
+ //
+ pList = ElfpAllocateBuffer(ListLength + sizeof(WCHAR));
+
+ //
+ // If I can't allocate the list, just press on
+ //
+
+ if (pList) {
+
+ //
+ // STEP 3: Copy all the module names for this logfile into
+ // the MULTI_SZ string.
+ //
+ SubKeyString = pList; // Save this away
+
+ pListEntry = LogModuleHead.Flink;
+
+ while (pListEntry != &LogModuleHead) {
+
+ pModule = CONTAINING_RECORD (
+ pListEntry,
+ LOGMODULE,
+ ModuleList
+ );
+
+ if (pModule->LogFile == pLogFile) {
+
+ //
+ // This one is for the log we're working on, put it in the list
+ //
+
+ wcscpy(pList, pModule->ModuleName);
+ pList += wcslen(pModule->ModuleName);
+ pList++;
+
+ }
+
+ pListEntry = pModule->ModuleList.Flink;
+
+ }
+
+ *pList = L'\0'; // The terminating NULL
+
+ RtlInitUnicodeString(&ListName, L"Sources");
+
+ Status = NtSetValueKey(hLogFile,
+ &ListName,
+ 0,
+ REG_MULTI_SZ,
+ SubKeyString,
+ ListLength + sizeof(WCHAR)
+ );
+
+ ElfpFreeBuffer(SubKeyString);
+ }
+ }
+
+ return(Status);
+
+}
+
+
+NTSTATUS
+ElfSetUpConfigDataStructs (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets up all the necessary data structures for the eventlog
+ service. It enumerates the keys in the Logfiles registry node to
+ determine what to setup.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hLogFile;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING SubKeyName;
+ PUNICODE_STRING pLogFileName = NULL;
+ PUNICODE_STRING pModuleName = NULL;
+ UNICODE_STRING EventlogModuleName;
+ ULONG Index = 0;
+ BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
+ PKEY_NODE_INFORMATION KeyBuffer = (PKEY_NODE_INFORMATION) Buffer;
+ ULONG ActualSize;
+ LOG_FILE_INFO LogFileInfo;
+ PWCHAR SubKeyString;
+ LPWSTR ModuleName;
+
+ ElfDbgPrint(("[ELF] Set up config data structures\n"));
+
+ //
+ // Initialize the Atom table whose size is the maximum number of
+ // module structures possible, i.e. ELF_MAX_LOG_MODULES.
+ //
+
+ if (! (InitAtomTable ( ELF_MAX_LOG_MODULES ))) {
+ return (STATUS_UNSUCCESSFUL);
+ }
+
+ //
+ // Get a handle to the Logfiles subkey. If it doesn't exist, just use
+ // the hard-coded defaults.
+ //
+
+ if (hEventLogNode) {
+
+ //
+ // Loop thru the subkeys under Eventlog and set up each logfile
+ //
+
+ while (NT_SUCCESS(Status)) {
+
+ Status = NtEnumerateKey(hEventLogNode, Index++, KeyNodeInformation,
+ KeyBuffer, ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // It turns out the Name isn't null terminated, so we need
+ // to copy it somewhere and null terminate it before we use it
+ //
+
+ SubKeyString = ElfpAllocateBuffer(KeyBuffer->NameLength +
+ sizeof(WCHAR));
+ if (!SubKeyString) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ memcpy(SubKeyString, KeyBuffer->Name, KeyBuffer->NameLength);
+ SubKeyString[KeyBuffer->NameLength / sizeof(WCHAR)] = L'\0' ;
+
+ //
+ // Open the node for this logfile and extract the information
+ // required by SetupDataStruct, and then call it.
+ //
+
+ RtlInitUnicodeString(&SubKeyName, SubKeyString);
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ hEventLogNode,
+ NULL
+ );
+
+ Status = NtOpenKey(&hLogFile, KEY_READ | KEY_SET_VALUE,
+ &ObjectAttributes);
+ if (!NT_SUCCESS(Status)) {
+ //
+ // Unclear how this could happen since I just enum'ed
+ // it, but if I can't open it, I just pretend like it
+ // wasn't there to begin with.
+ //
+
+ ElfpFreeBuffer(SubKeyString);
+ Status = STATUS_SUCCESS; // so we don't terminate the loop
+ continue;
+ }
+
+ //
+ // Get the information from the registry
+ //
+
+ Status = ReadRegistryInfo(hLogFile, &SubKeyName,
+ & LogFileInfo);
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Now set up the actual data structures. Failures are
+ // dealt with in the routine
+ //
+
+ SetUpDataStruct(LogFileInfo.LogFileName,
+ LogFileInfo.MaxFileSize,
+ LogFileInfo.Retention,
+ LogFileInfo.GuestAccessRestriction,
+ & SubKeyName,
+ hLogFile,
+ ElfNormalLog
+ );
+
+ NtClose(hLogFile);
+
+ }
+ }
+
+ }
+ } // if (hEventLogNode)
+ else {
+
+ //
+ // The information doesn't exist in the registry, set up the
+ // three default logs.
+ //
+
+ pLogFileName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ pModuleName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ if (!pLogFileName || !pModuleName) {
+ return(STATUS_NO_MEMORY);
+ }
+ RtlInitUnicodeString(pLogFileName,
+ ELF_APPLICATION_DEFAULT_LOG_FILE);
+ RtlInitUnicodeString(pModuleName, ELF_DEFAULT_MODULE_NAME);
+ SetUpDataStruct(pLogFileName,
+ ELF_DEFAULT_MAX_FILE_SIZE,
+ ELF_DEFAULT_RETENTION_PERIOD,
+ ELF_GUEST_ACCESS_UNRESTRICTED,
+ pModuleName,
+ NULL,
+ ElfNormalLog
+ );
+
+
+ pLogFileName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ pModuleName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ if (!pLogFileName || !pModuleName) {
+ return(STATUS_NO_MEMORY);
+ }
+ RtlInitUnicodeString(pLogFileName,
+ ELF_SYSTEM_DEFAULT_LOG_FILE);
+ RtlInitUnicodeString(pModuleName, ELF_SYSTEM_MODULE_NAME);
+ SetUpDataStruct(pLogFileName,
+ ELF_DEFAULT_MAX_FILE_SIZE,
+ ELF_DEFAULT_RETENTION_PERIOD,
+ ELF_GUEST_ACCESS_UNRESTRICTED,
+ pModuleName,
+ NULL,
+ ElfNormalLog
+ );
+
+
+ pLogFileName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ pModuleName = ElfpAllocateBuffer(sizeof(UNICODE_STRING));
+ if (!pLogFileName || !pModuleName) {
+ return(STATUS_NO_MEMORY);
+ }
+ RtlInitUnicodeString(pLogFileName,
+ ELF_SECURITY_DEFAULT_LOG_FILE);
+ RtlInitUnicodeString(pModuleName, ELF_SECURITY_MODULE_NAME);
+ SetUpDataStruct(pLogFileName,
+ ELF_DEFAULT_MAX_FILE_SIZE,
+ ELF_DEFAULT_RETENTION_PERIOD,
+ ELF_GUEST_ACCESS_UNRESTRICTED,
+ pModuleName,
+ NULL,
+ ElfNormalLog
+ );
+ }
+
+ //
+ // If we just ran out of keys, that's OK (unless there weren't any at all)
+ //
+
+ if (Status == STATUS_NO_MORE_ENTRIES && Index != 1) {
+ Status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Make sure we created the Application log file, since it is the
+ // default. If it wasn't created, use the first module created
+ // (this is at the tail of the list since I insert them at the
+ // head). If this happens, send an alert to the admin.
+ //
+
+ if (!ElfDefaultLogModule) {
+ ElfDbgPrintNC(("[ELF] No Logfile entry for Application module, "
+ "default will be created\n"));
+
+ if (IsListEmpty(&LogModuleHead)) {
+ //
+ // No logs were created, might as well shut down
+ //
+
+ return(STATUS_EVENTLOG_CANT_START);
+ }
+
+ ElfDefaultLogModule = CONTAINING_RECORD(LogModuleHead.Blink,
+ LOGMODULE,
+ ModuleList);
+
+ ModuleName = L"Application";
+ ElfpCreateQueuedAlert(ALERT_ELF_DefaultLogCorrupt, 1,
+ &(ElfDefaultLogModule->LogFile->LogModuleName->Buffer));
+
+ }
+
+ //
+ // Now get the Module for the Eventlog service to use. GetModuleStruc
+ // always succeeds, returning the default log if the requested one
+ // isn't configured.
+ //
+
+ RtlInitUnicodeString(&EventlogModuleName, L"eventlog");
+ ElfModule = GetModuleStruc(&EventlogModuleName);
+
+ } else {
+
+ if (pLogFileName && pModuleName) {
+ ElfDbgPrintNC(("[ELF] Failure Setting up data structs for file %ws, "
+ "Module %ws - %X\n", pLogFileName->Buffer, pModuleName->Buffer,
+ Status));
+ }
+ else {
+ ElfDbgPrintNC(("[ELF] Failure setting up data structs. No logs"
+ " defined in registry\n"));
+ }
+ }
+
+
+ return (Status);
+
+}
+
+
+VOID
+SVCS_ENTRY_POINT( // (ELF_main)
+ DWORD argc,
+ LPWSTR argv[],
+ PSVCS_GLOBAL_DATA SvcsGlobalData,
+ HANDLE SvcRefHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This is the main routine for the Event Logging Service.
+
+
+Arguments:
+
+ Command-line arguments.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING RootRegistryNode;
+ ULONG Win32Error = 0;
+ ELF_REQUEST_RECORD FlushRequest;
+ UNICODE_STRING ValueName;
+ BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
+ PKEY_VALUE_FULL_INFORMATION ValueBuffer =
+ (PKEY_VALUE_FULL_INFORMATION) Buffer;
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+
+ UNREFERENCED_PARAMETER(argc);
+ UNREFERENCED_PARAMETER(argv);
+
+ ElfGlobalSvcRefHandle = SvcRefHandle;
+ ElfGlobalData = SvcsGlobalData;
+
+ //
+ // Initialize the list heads for the modules and log files.
+ //
+
+ InitializeListHead ( &LogFilesHead );
+ InitializeListHead ( &LogModuleHead );
+ InitializeListHead ( &QueuedEventListHead );
+ InitializeListHead ( &QueuedMessageListHead );
+
+ //
+ // Initialize to 0 so that we can clean up before exiting
+ //
+
+ EventFlags = 0;
+
+ //
+ //
+ // Tuck away the local computer name
+ //
+
+ ComputerNameLength = 0;
+ GetComputerNameW(LocalComputerName, &ComputerNameLength);
+ ComputerNameLength += sizeof(WCHAR); // account for the NULL
+ LocalComputerName = ElfpAllocateBuffer(ComputerNameLength * sizeof (WCHAR));
+ if (!LocalComputerName ||
+ !GetComputerNameW(LocalComputerName, &ComputerNameLength)) {
+ ComputerNameLength = 0;
+ }
+ ComputerNameLength = (ComputerNameLength + 1) * sizeof(WCHAR);
+
+ //
+ // Initialize the status data.
+ //
+ ElInitStatus();
+
+ // Set up control handler
+ //
+
+ ElfDbgPrint(("[ELF] Calling RegisterServiceCtrlHandler\n"));
+ if ((ElfServiceStatusHandle = RegisterServiceCtrlHandler(
+ wname_Eventlogsvc,
+ ElfControlResponse
+ )) == (SERVICE_STATUS_HANDLE) NULL) {
+
+ Win32Error = GetLastError();
+
+ //
+ // If we got an error, we need to set status to uninstalled, and end the
+ // thread.
+ //
+
+ ElfDbgPrintNC(("[ELF] RegisterServiceCtrlHandler = %u\n",Win32Error));
+ goto cleanupandexit;
+ }
+
+ // Initialize all the status fields so that subsequent calls to
+ // SetServiceStatus only need to update fields that changed.
+ //
+
+ //
+ // Notify the Service Controller for the first time that we are alive
+ // and is in a start pending state
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+ //
+ // Get the localized title for message box popups.
+ //
+ ElfInitMessageBoxTitle();
+
+ //
+ // Set up the object that describes the root node for the eventlog service
+ //
+
+ RtlInitUnicodeString(&RootRegistryNode, REG_EVENTLOG_NODE_PATH);
+ InitializeObjectAttributes(&ObjectAttributes,
+ &RootRegistryNode,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ //
+ // If this fails, we'll just use the defaults
+ //
+
+ NtOpenKey(&hEventLogNode, KEY_READ | KEY_NOTIFY, &ObjectAttributes);
+
+ //
+ // See if there's a debug key
+ //
+
+ RtlInitUnicodeString(&ValueName, VALUE_DEBUG);
+
+ NtQueryValueKey(hEventLogNode, &ValueName,
+ KeyValueFullInformation, ValueBuffer,
+ ELF_MAX_REG_KEY_INFO_SIZE, & ElfDebug);
+
+ //
+ // Initialize a critical section for use when adding or removing
+ // LogFiles or LogModules. This must be done before we process any
+ // file information.
+ //
+
+ Status = RtlInitializeCriticalSection(
+ &LogFileCritSec
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(( "ELF log file crit sec init failed: %X\n", Status ));
+ goto cleanupandexit;
+
+ }
+ Status = RtlInitializeCriticalSection(
+ &LogModuleCritSec
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(( "ELF log file crit sec init failed: %X\n", Status ));
+ goto cleanupandexit;
+
+ }
+ EventFlags |= ELF_INIT_LOGFILE_CRIT_SEC;
+
+ Status = RtlInitializeCriticalSection(
+ &QueuedEventCritSec
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(( "ELF queued event crit sec init failed: %X\n", Status ));
+ goto cleanupandexit;
+
+ }
+
+ EventFlags |= ELF_INIT_QUEUED_EVENT_CRIT_SEC;
+
+ Status = RtlInitializeCriticalSection(
+ &QueuedMessageCritSec
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(( "ELF queued message crit sec init failed: %X\n", Status ));
+ goto cleanupandexit;
+
+ }
+
+ EventFlags |= ELF_INIT_QUEUED_MESSAGE_CRIT_SEC;
+
+ //
+ // Initialize global anonymous logon sid for use in log ACL's.
+ //
+
+ Status = RtlAllocateAndInitializeSid(
+ &NtAuthority,
+ 1,
+ SECURITY_ANONYMOUS_LOGON_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &AnonymousLogonSid);
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(("ELF anonymous log sid creation failed: %X\n",
+ Status ));
+ goto cleanupandexit;
+ }
+
+ //
+ // Set up the data structures for the Logfiles and Modules.
+ //
+
+ Status = ElfSetUpConfigDataStructs ();
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto cleanupandexit;
+ }
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+ //
+ // Initialize a critical section for use when adding or removing
+ // context-handles (LogHandles).
+ //
+
+ Status = RtlInitializeCriticalSection(
+ &LogHandleCritSec
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ ElfDbgPrintNC(( "ELF log handle crit sec init failed: %X\n", Status ));
+ goto cleanupandexit;
+
+ }
+ EventFlags |= ELF_INIT_LOGHANDLE_CRIT_SEC;
+
+ //
+ // Initialize the context handle (log handle) list.
+ //
+
+ InitializeListHead( &LogHandleListHead );
+
+ //
+ // Initialize the Global Resource.
+ //
+
+ RtlInitializeResource ( &GlobalElfResource );
+ EventFlags |= ELF_INIT_GLOBAL_RESOURCE;
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+ // Create a thread for watching the LPC port.
+ //
+
+ if (!StartLPCThread ()) {
+ Status = STATUS_UNSUCCESSFUL;
+ goto cleanupandexit;
+ }
+ EventFlags |= ELF_STARTED_LPC_THREAD;
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+#ifdef _CAIRO_
+ //
+ // The eventlog service links to ALERTSYS.DLL by hand (eventlog.c) after
+ // eventlog initialization, since this dll's initialization code requires
+ // a running eventlog service.
+ //
+ // By no means, fail to start this service if something fails here.
+ // It just won't be possible to raise NT events as Cairo alerts.
+ //
+ // BUGBUG : Should probably at least log an error.
+ // Should the service state be STARTING while this
+ // initialization is in progress?
+ //
+
+ if ((ghAlertSysDll = LoadLibrary(L"ALERTSYS.DLL")) != NULL)
+ {
+ //
+ // Get ReportAlert API address.
+ //
+
+ if ((gpfReportAlert = (PREPORTALERT)GetProcAddress(
+ (HMODULE)ghAlertSysDll,
+ "ReportAlert")) == NULL)
+ {
+ FreeLibrary(ghAlertSysDll);
+ ghAlertSysDll = NULL;
+ ElfDbgPrintNC((
+ "[ELF] ReportAlert GetProAddress failed, WIN32 error(%x)\n",
+ GetLastError()));
+ }
+ }
+ else
+ {
+ ElfDbgPrintNC((
+ "[ELF] LoadLibrary of ALERTSYS.DLL failed, WIN32 error(%x)\n",
+ GetLastError()));
+ }
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+#endif // _CAIRO_
+
+ // Create a thread for watching for changes in the registry.
+ //
+
+ if (!ElfStartRegistryMonitor ()) {
+ Status = STATUS_UNSUCCESSFUL;
+ goto cleanupandexit;
+ }
+ EventFlags |= ELF_STARTED_REGISTRY_MONITOR;
+
+ //
+ // Write out an event that says we started
+ //
+
+ ElfpCreateElfEvent(EVENT_EventlogStarted,
+ EVENTLOG_INFORMATION_TYPE,
+ 0, // EventCategory
+ 0, // NumberOfStrings
+ NULL, // Strings
+ NULL, // Data
+ 0, // Datalength
+ 0 // flags
+ );
+
+ //
+ // Write out any events that were queued up during initialization
+ //
+
+ FlushRequest.Command = ELF_COMMAND_WRITE_QUEUED;
+
+ ElfPerformRequest(&FlushRequest);
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ // *** UPDATE STATUS ***
+ ElfStatusUpdate(STARTING);
+
+ //
+ // Finish setting up the RPC server
+ //
+ // NOTE: Now all RPC servers in services.exe share the same pipe name.
+ // However, in order to support communication with version 1.0 of WinNt,
+ // it is necessary for the Client Pipe name to remain the same as
+ // it was in version 1.0. Mapping to the new name is performed in
+ // the Named Pipe File System code.
+ //
+ Status = ElfGlobalData->StartRpcServer(
+ ElfGlobalData->SvcsRpcPipeName,
+ eventlog_ServerIfHandle);
+
+ if (Status != NO_ERROR) {
+ ElfDbgPrint(("[ELF]StartRpcServer Failed %d\n",Status));
+ goto cleanupandexit;
+ }
+
+ //
+ // Tell service controller of that we are making progress
+ //
+
+ if (ElfStatusUpdate(RUNNING) == RUNNING) {
+ ElfDbgPrint(("[ELF] Service Started Successfully\n"));
+ }
+
+ EventFlags |= ELF_STARTED_RPC_SERVER;
+
+ if (GetElState() == RUNNING) {
+ ElfDbgPrint(("[ELF] Service Running - main thread is returning\n"));
+ return;
+ }
+
+//
+// Come here if there is cleanup necessary.
+//
+
+cleanupandexit:
+
+ ElfDbgPrint(("[ELF] Leaving the service\n"));
+
+ if (!Win32Error) {
+ Win32Error = RtlNtStatusToDosError(Status);
+ }
+ ElfBeginForcedShutdown(PENDING,Win32Error,Status);
+
+ //
+ // If the registry monitor has been initialized, then
+ // let it do the shutdown cleanup. All we need to do
+ // here is wake it up.
+ // Otherwise, this thread will do the cleanup.
+ //
+ if (EventFlags & ELF_STARTED_REGISTRY_MONITOR) {
+ StopRegistryMonitor();
+ }
+ else {
+ ElfpCleanUp(EventFlags);
+ //
+ // We should actually return here so that the DLL gets unloaded.
+ // However, RPC has a problem in that it might still call our
+ // context rundown routine even though we unregistered our interface.
+ // So we exit thread instead. This keeps our Dll loaded.
+ //
+ ExitThread(0);
+ }
+ return;
+}
+
+VOID
+ElfInitMessageBoxTitle(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Obtains the title text for the message box used to display messages.
+ If the title is successfully obtained from the message file, then
+ that title is pointed to by GlobalAllocatedMsgTitle and
+ GlobalMessageBoxTitle. If unsuccessful, then GlobalMessageBoxTitle
+ left pointing to the DefaultMessageBoxTitle.
+
+ NOTE: If successful, a buffer is allocated by this function. The
+ pointer stored in GlobalAllocatedMsgTitle and it should be freed when
+ done with this buffer.
+
+Arguments:
+
+Return Value:
+
+ none
+
+--*/
+{
+ LPVOID hModule;
+ DWORD msgSize;
+ DWORD status=NO_ERROR;
+
+ GlobalAllocatedMsgTitle = NULL;
+
+ hModule = LoadLibrary( L"netevent.dll");
+ if ( hModule == NULL) {
+ status = GetLastError();
+ ElfDbgPrint(("LoadLibrary() fails with winError = %d\n", GetLastError()));
+ return;
+ }
+ msgSize = FormatMessageW(
+ FORMAT_MESSAGE_FROM_HMODULE | // dwFlags
+ FORMAT_MESSAGE_ARGUMENT_ARRAY |
+ FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ hModule,
+ TITLE_EventlogMessageBox, // MessageId
+ 0, // dwLanguageId
+ (LPWSTR)&GlobalAllocatedMsgTitle, // lpBuffer
+ 0, // nSize
+ NULL);
+
+ if (msgSize == 0) {
+ status = GetLastError();
+ ElfDbgPrint((ERROR,"Could not find MessageBox title in a message file %d\n",
+ status));
+ }
+ else {
+ GlobalMessageBoxTitle = GlobalAllocatedMsgTitle;
+ }
+
+ FreeLibrary(hModule);
+ return;
+}
+
+
+
diff --git a/private/eventlog/server/eventlog.def b/private/eventlog/server/eventlog.def
new file mode 100644
index 000000000..987b75f56
--- /dev/null
+++ b/private/eventlog/server/eventlog.def
@@ -0,0 +1,6 @@
+NAME eventlog.dll
+
+DESCRIPTION 'NT Event Logging Service'
+
+EXPORTS
+ ServiceEntry
diff --git a/private/eventlog/server/eventlog.rc b/private/eventlog/server/eventlog.rc
new file mode 100644
index 000000000..19b4d3553
--- /dev/null
+++ b/private/eventlog/server/eventlog.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Event Logging Service"
+#define VER_INTERNALNAME_STR "Eventlog.DLL"
+#define VER_ORIGINALFILENAME_STR "Eventlog.DLL"
+
+#include "common.ver"
+
diff --git a/private/eventlog/server/eventp.h b/private/eventlog/server/eventp.h
new file mode 100644
index 000000000..ccb968cce
--- /dev/null
+++ b/private/eventlog/server/eventp.h
@@ -0,0 +1,48 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ eventp.h
+
+Abstract:
+
+ Private include file for eventlog service
+
+Author:
+
+ Rajen Shah (rajens) 12-Jul-1991
+
+Revision History:
+
+--*/
+
+#ifndef _CAIRO_
+#define UNICODE // This service uses unicode APIs
+#endif // _CAIRO_
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntelfapi.h>
+#include <netevent.h> // Manifest constants for Events
+
+#include <windows.h>
+#include <winsvc.h>
+
+#include <lmcons.h>
+#include <lmerr.h>
+#include <rpc.h>
+#include <svcs.h> // SVCS_ENTRY_POINT, PSVCS_GLOBAL_DATA
+
+#ifdef _CAIRO_
+#include <wtypes.h>
+#endif // _CAIRO_
+
+#include <elf.h>
+
+#include <elfdef.h>
+#include <elfcommn.h>
+#include <elfproto.h>
+#include <elfextrn.h>
diff --git a/private/eventlog/server/file.c b/private/eventlog/server/file.c
new file mode 100644
index 000000000..63bc7d025
--- /dev/null
+++ b/private/eventlog/server/file.c
@@ -0,0 +1,1088 @@
+/*
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ FILE.C
+
+Abstract:
+
+ This file contains the routines that deal with file-related operations.
+
+Author:
+
+ Rajen Shah (rajens) 07-Aug-1991
+
+Revision History:
+
+ 29-Aug-1994 Danl
+ We no longer grow log files in place. So there is no need to
+ reserve the MaxConfigSize block of memory.
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <alertmsg.h> // ALERT_ELF manifests
+
+//
+// Macros
+//
+
+#define IS_EOF(Ptr, Size) \
+ (Ptr)->Length == ELFEOFRECORDSIZE && \
+ RtlCompareMemory (Ptr, &EOFRecord, Size) == Size
+
+#ifdef CORRUPTED
+
+
+BOOLEAN
+VerifyLogIntegrity(
+ PLOGFILE pLogFile
+ )
+/*++
+
+Routine Description:
+
+ This routine walks the log file to verify that it isn't corrupt
+
+
+Arguments:
+
+ A pointer to the LOGFILE structure for the log to validate.
+
+Return Value:
+
+ TRUE if log OK
+ FALSE if it is corrupt
+
+Note:
+
+
+--*/
+{
+
+ PEVENTLOGRECORD pEventLogRecord;
+ PVOID PhysicalStart;
+ PVOID PhysicalEOF;
+ PVOID BeginRecord;
+ PVOID EndRecord;
+
+ pEventLogRecord =
+ (PEVENTLOGRECORD)((PBYTE) pLogFile->BaseAddress + pLogFile->BeginRecord);
+ PhysicalStart =
+ (PVOID) ((PBYTE) pLogFile->BaseAddress + FILEHEADERBUFSIZE);
+ PhysicalEOF =
+ (PVOID) ((PBYTE) pLogFile->BaseAddress + pLogFile->ViewSize);
+ BeginRecord = (PVOID)((PBYTE) pLogFile->BaseAddress + pLogFile->BeginRecord);
+ EndRecord = (PVOID)((PBYTE) pLogFile->BaseAddress + pLogFile->EndRecord);
+
+ while(pEventLogRecord->Length != ELFEOFRECORDSIZE) {
+
+ pEventLogRecord = (PEVENTLOGRECORD) NextRecordPosition (
+ EVENTLOG_FORWARDS_READ,
+ (PVOID) pEventLogRecord,
+ pEventLogRecord->Length,
+ BeginRecord,
+ EndRecord,
+ PhysicalEOF,
+ PhysicalStart
+ );
+
+ if (!pEventLogRecord || pEventLogRecord->Length == 0) {
+
+ ElfDbgPrintNC(("[ELF] The %ws logfile is corrupt\n",
+ pLogFile->LogModuleName->Buffer));
+ return(FALSE);
+ }
+ }
+
+ return(TRUE);
+
+}
+
+#endif // CORRUPTED
+
+
+NTSTATUS
+FlushLogFile (
+ PLOGFILE pLogFile
+ )
+
+/*++
+
+Routine Description:
+
+ This routine flushes the file specified. It updates the file header,
+ and then flushes the virtual memory which causes the data to get
+ written to disk.
+
+Arguments:
+
+ pLogFile points to the log file structure.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PVOID BaseAddress;
+ ULONG RegionSize;
+ PELF_LOGFILE_HEADER pLogFileHeader;
+
+ //
+ // If the dirty bit is set, update the file header before flushing it.
+ //
+ if (pLogFile->Flags & ELF_LOGFILE_HEADER_DIRTY) {
+
+ pLogFileHeader = (PELF_LOGFILE_HEADER) pLogFile->BaseAddress;
+
+ pLogFile->Flags &= ~ELF_LOGFILE_HEADER_DIRTY; // Remove dirty bit
+ pLogFileHeader->Flags = pLogFile->Flags;
+
+ pLogFileHeader->StartOffset = pLogFile->BeginRecord;
+ pLogFileHeader->EndOffset = pLogFile->EndRecord;
+ pLogFileHeader->CurrentRecordNumber = pLogFile->CurrentRecordNumber;
+ pLogFileHeader->OldestRecordNumber = pLogFile->OldestRecordNumber;
+ }
+
+ //
+ // Flush the memory in the section that is mapped to the file.
+ //
+ BaseAddress = pLogFile->BaseAddress;
+ RegionSize = pLogFile->ViewSize;
+
+ Status = NtFlushVirtualMemory(
+ NtCurrentProcess(),
+ &BaseAddress,
+ &RegionSize,
+ &IoStatusBlock
+ );
+
+ return (Status);
+
+}
+
+
+
+NTSTATUS
+ElfpFlushFiles (
+ )
+
+/*++
+
+Routine Description:
+
+ This routine flushes all the log files and forces them on disk.
+ It is usually called in preparation for a shutdown or a pause.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+
+{
+
+ PLOGFILE pLogFile;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ //
+ // Make sure that there's at least one file to flush
+ //
+
+ if (IsListEmpty (&LogFilesHead) ) {
+ return(STATUS_SUCCESS);
+ }
+
+ pLogFile
+ = (PLOGFILE)
+ CONTAINING_RECORD(LogFilesHead.Flink, LOGFILE, FileList);
+
+ //
+ // Go through this loop at least once. This ensures that the termination
+ // condition will work correctly.
+ //
+ do {
+
+ Status = FlushLogFile (pLogFile);
+
+ pLogFile = // Get next one
+ (PLOGFILE)
+ CONTAINING_RECORD(pLogFile->FileList.Flink, LOGFILE, FileList);
+
+ } while ( (pLogFile->FileList.Flink != LogFilesHead.Flink)
+ && (NT_SUCCESS(Status)) ) ;
+
+ return (Status);
+}
+
+
+
+
+NTSTATUS
+ElfpCloseLogFile (
+ PLOGFILE pLogFile,
+ DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+ This routine undoes whatever is done in ElfOpenLogFile.
+
+Arguments:
+
+ pLogFile points to the log file structure.
+
+Return Value:
+
+ NTSTATUS.
+
+Note:
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PELF_LOGFILE_HEADER pLogFileHeader;
+ PVOID BaseAddress;
+ ULONG Size;
+
+ ElfDbgPrint(("[ELF] Closing and unmapping log file: %ws\n",
+ pLogFile->LogFileName->Buffer));
+
+#ifdef CORRUPTED
+
+ //
+ // Just for debugging a log corruption problem
+ //
+
+ if (!VerifyLogIntegrity(pLogFile)) {
+ ElfDbgPrintNC(("[ELF] Integrity check failed in ElfpCloseLogFile\n"));
+ }
+
+#endif // CORRUPTED
+
+ //
+ // If the dirty bit is set, update the file header before closing it.
+ // Check to be sure it's not a backup file that just had the dirty
+ // bit set when it was copied.
+ //
+
+ if (pLogFile->Flags & ELF_LOGFILE_HEADER_DIRTY &&
+ !(Flags & ELF_LOG_CLOSE_BACKUP)) {
+ pLogFileHeader = (PELF_LOGFILE_HEADER) pLogFile->BaseAddress;
+ pLogFileHeader->StartOffset = pLogFile->BeginRecord;
+ pLogFileHeader->EndOffset = pLogFile->EndRecord;
+ pLogFileHeader->CurrentRecordNumber = pLogFile->CurrentRecordNumber;
+ pLogFileHeader->OldestRecordNumber = pLogFile->OldestRecordNumber;
+ pLogFile->Flags &= ~(ELF_LOGFILE_HEADER_DIRTY |
+ ELF_LOGFILE_ARCHIVE_SET); // Remove dirty &
+ // archive bits
+ pLogFileHeader->Flags = pLogFile->Flags;
+ }
+
+ //
+ // Decrement the reference count, and if it goes to zero, unmap the file
+ // and close the handle. Also force the close if fForceClosed is TRUE.
+ //
+
+ if ((--pLogFile->RefCount == 0) || Flags & ELF_LOG_CLOSE_FORCE) {
+
+ //
+ // Last user has gone.
+ // Close all the views and the file and section handles. Free up
+ // any extra memory we had reserved, and unlink any structures
+ //
+
+ if (pLogFile->BaseAddress) // Unmap it if it was allocated
+ NtUnmapViewOfSection (
+ NtCurrentProcess(),
+ pLogFile->BaseAddress
+ );
+
+ if (pLogFile->SectionHandle)
+ NtClose ( pLogFile->SectionHandle );
+
+ if (pLogFile->FileHandle)
+ NtClose ( pLogFile->FileHandle );
+ }
+
+ return (Status);
+
+} // ElfpCloseLogFile
+
+
+NTSTATUS
+RevalidateLogHeader (
+ PELF_LOGFILE_HEADER pLogFileHeader,
+ PLOGFILE pLogFile
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine is called if we encounter a "dirty" log file. The
+ routine walks through the file until it finds a signature for a valid log
+ record. It then walks forward thru the file until it finds the EOF
+ record, or an invalid record. Then it walks backwards from the first
+ record it found until it finds the EOF record from the other direction.
+ It then rebuilds the header and writes it back to the log. If it can't
+ find any valid records, it rebuilds the header to reflect an empty log
+ file. If it finds a trashed file, it writes 256 bytes of the log out in
+ an event to the system log.
+
+Arguments:
+
+ pLogFileHeader points to the header of the log file.
+ pLogFile points to the log file structure.
+
+Return Value:
+
+ NTSTATUS.
+
+Note:
+
+ This is an expensive routine since it scans the entire file.
+
+ It assumes that the records are on DWORD boundaries.
+
+--*/
+{
+ PVOID Start, End;
+ PDWORD pSignature;
+ PEVENTLOGRECORD pEvent;
+ PEVENTLOGRECORD FirstRecord;
+ PEVENTLOGRECORD FirstPhysicalRecord;
+ PEVENTLOGRECORD pLastGoodRecord;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ LARGE_INTEGER ByteOffset;
+ ULONG Size;
+
+ ElfDbgPrint(("[ELF] Log file had dirty bit set, revalidating\n"));
+
+ try {
+
+ //
+ // Physical start and end of log file (skipping header)
+ //
+
+ Start = (PVOID) ((PBYTE)pLogFile->BaseAddress + FILEHEADERBUFSIZE);
+ End = (PVOID) ((PBYTE)pLogFile->BaseAddress +
+ pLogFile->ActualMaxFileSize);
+
+ //
+ // First see if the log has wrapped. The EOFRECORDSIZE is for the one
+ // case where the EOF record wraps so that it's final length just replaces
+ // the next records starting length
+ //
+
+ pEvent = (PEVENTLOGRECORD) Start;
+
+ if (pEvent->Reserved != ELF_LOG_FILE_SIGNATURE
+ || pEvent->RecordNumber != 1 || pEvent->Length == ELFEOFRECORDSIZE) {
+
+ //
+ // Log has already wrapped, go looking for the first valid record
+ //
+
+ for (pSignature = (PDWORD) Start; (PVOID)pSignature < End;
+ pSignature++) {
+
+ if (*pSignature == ELF_LOG_FILE_SIGNATURE) {
+
+ //
+ // Make sure it's really a record
+ //
+
+ pEvent = CONTAINING_RECORD(pSignature, EVENTLOGRECORD,
+ Reserved);
+ if (!ValidFilePos(pEvent, Start, End, End,
+ pLogFileHeader)) {
+ //
+ // Nope, not really, keep trying
+ //
+
+ continue;
+ }
+
+ //
+ // This is a valid record, Remember this so you can use
+ // it later
+ //
+
+ FirstPhysicalRecord = pEvent;
+
+ //
+ // Walk backwards from here (wrapping if necessary) until
+ // you hit the EOF record or an invalid record.
+ //
+
+ while (pEvent && ValidFilePos(pEvent, Start, End, End, pLogFileHeader)) {
+
+ //
+ // See if it's the EOF record
+ //
+
+ if (IS_EOF(pEvent,
+ min (ELFEOFUNIQUEPART,
+ (PBYTE) End - (PBYTE) pEvent))) {
+
+ break;
+ }
+
+ pLastGoodRecord = pEvent;
+ pEvent = NextRecordPosition (
+ EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ,
+ pEvent,
+ pEvent->Length,
+ 0,
+ 0,
+ End,
+ Start);
+
+ //
+ // Make sure we're not in an infinite loop
+ //
+
+ if (pEvent == FirstPhysicalRecord) {
+ return(STATUS_UNSUCCESSFUL);
+ }
+ }
+
+ //
+ // Found the first record, now go look for the last
+ //
+
+ FirstRecord = pLastGoodRecord;
+ break;
+ }
+ }
+
+ if (pSignature == End) {
+
+ //
+ // We couldn't find one valid record in the whole log. Give
+ // up and we'll set it to an empty log file
+ //
+
+ return(STATUS_UNSUCCESSFUL);
+ }
+ }
+ else {
+
+ //
+ // We haven't wrapped yet
+ //
+
+ FirstPhysicalRecord = FirstRecord = Start;
+ }
+
+
+ //
+ // Now read forward looking for the last good record
+ //
+
+ pEvent = FirstPhysicalRecord;
+
+ while (pEvent && ValidFilePos(pEvent, Start, End, End, pLogFileHeader)) {
+
+ //
+ // See if it's the EOF record
+ //
+
+ if (IS_EOF(pEvent, min (ELFEOFUNIQUEPART,
+ (PBYTE) End - (PBYTE) pEvent))) {
+
+ break;
+ }
+
+ pLastGoodRecord = pEvent;
+ pEvent = NextRecordPosition (
+ EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
+ pEvent,
+ pEvent->Length,
+ 0,
+ 0,
+ End,
+ Start);
+
+ //
+ // Make sure we're not in an infinite loop
+ //
+
+ if (pEvent == FirstPhysicalRecord) {
+ return(STATUS_UNSUCCESSFUL);
+ }
+ }
+
+ //
+ // Now we know the first record (FirstRecord) and the last record
+ // (pLastGoodRecord) so we can create the header an EOF record and
+ // write them out (EOF record gets written at pEvent)
+ //
+ // First the EOF record
+ //
+
+ EOFRecord.BeginRecord = (PBYTE) FirstRecord - (PBYTE) pLogFileHeader;
+ EOFRecord.EndRecord = (PBYTE) pEvent - (PBYTE) pLogFileHeader;
+ EOFRecord.CurrentRecordNumber =
+ pLastGoodRecord->RecordNumber + 1;
+ EOFRecord.OldestRecordNumber = FirstRecord->RecordNumber;
+
+ ByteOffset = RtlConvertUlongToLargeInteger (
+ (PBYTE) pEvent - (PBYTE) pLogFileHeader);
+ Status = NtWriteFile(
+ pLogFile->FileHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &EOFRecord, // Buffer
+ ELFEOFRECORDSIZE, // Length
+ &ByteOffset, // Byteoffset
+ NULL); // Key
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrint(("[ELF]: Log file header write failed 0x%lx\n",
+ Status));
+ return (Status);
+ }
+
+ //
+ // Now the header
+ //
+
+ pLogFileHeader->StartOffset = (PBYTE) FirstRecord- (PBYTE) pLogFileHeader;
+ pLogFileHeader->EndOffset = (PBYTE) pEvent- (PBYTE) pLogFileHeader;
+ pLogFileHeader->CurrentRecordNumber =
+ pLastGoodRecord->RecordNumber + 1;
+ pLogFileHeader->OldestRecordNumber = FirstRecord->RecordNumber;
+ pLogFileHeader->HeaderSize = pLogFileHeader->EndHeaderSize = FILEHEADERBUFSIZE;
+ pLogFileHeader->Signature = ELF_LOG_FILE_SIGNATURE;
+ pLogFileHeader->Flags = 0;
+
+ if (pLogFileHeader->StartOffset != FILEHEADERBUFSIZE)
+ pLogFileHeader->Flags |= ELF_LOGFILE_HEADER_WRAP;
+
+ pLogFileHeader->MaxSize = pLogFile->ActualMaxFileSize;
+ pLogFileHeader->Retention = pLogFile->Retention;
+ pLogFileHeader->MajorVersion = ELF_VERSION_MAJOR;
+ pLogFileHeader->MinorVersion = ELF_VERSION_MINOR;
+
+ //
+ // Now flush this to disk to commit it
+ //
+
+ Start = pLogFile->BaseAddress;
+ Size = FILEHEADERBUFSIZE;
+
+ Status = NtFlushVirtualMemory(
+ NtCurrentProcess(),
+ &Start,
+ &Size,
+ &IoStatusBlock
+ );
+
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ return (Status);
+
+}
+
+
+NTSTATUS
+ElfOpenLogFile (
+ PLOGFILE pLogFile,
+ ELF_LOG_TYPE LogType
+ )
+
+/*++
+
+Routine Description:
+
+ Open the log file, create it if it does not exist.
+ Create a section and map a view into the log file.
+ Write out the header to the file, if it is newly created.
+ If "dirty", update the "start" and "end" pointers by scanning
+ the file. Set AUTOWRAP if the "start" does not start right after
+ the file header.
+
+Arguments:
+
+ pLogFile points to the log file structure, with the relevant data
+ filled in.
+
+ CreateOptions are the options to be passed to NtCreateFile which
+ indicate whether to open an existing file, or to create it
+ if it does not exist.
+
+Return Value:
+
+ NTSTATUS.
+
+Note:
+
+ It is up to the caller to set the RefCount in the Logfile structure.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ LARGE_INTEGER MaximumSizeOfSection;
+ LARGE_INTEGER ByteOffset;
+ PELF_LOGFILE_HEADER pLogFileHeader;
+ FILE_STANDARD_INFORMATION FileStandardInfo;
+ ULONG IoStatusInformation;
+ ULONG FileDesiredAccess;
+ ULONG SectionDesiredAccess;
+ ULONG SectionPageProtection;
+ ULONG CreateOptions;
+ ULONG CreateDisposition;
+
+ //
+ // File header in a new file has the "Start" and "End" pointers the
+ // same since there are no records in the file.
+ //
+
+ static ELF_LOGFILE_HEADER FileHeaderBuf = {FILEHEADERBUFSIZE, // Size
+ ELF_LOG_FILE_SIGNATURE,
+ ELF_VERSION_MAJOR,
+ ELF_VERSION_MINOR,
+ FILEHEADERBUFSIZE, // Start offset
+ FILEHEADERBUFSIZE, // End offset
+ 1, // Next record #
+ 0, // Oldest record #
+ 0, // Maxsize
+ 0, // Flags
+ 0, // Retention
+ FILEHEADERBUFSIZE // Size
+ };
+
+ //
+ // Set the file open and section create options based on the type of log
+ // file this is.
+ //
+
+ switch (LogType) {
+
+ case ElfNormalLog:
+ CreateDisposition = FILE_OPEN_IF;
+ FileDesiredAccess = GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE;
+ SectionDesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE |
+ SECTION_QUERY | SECTION_EXTEND_SIZE;
+ SectionPageProtection = PAGE_READWRITE;
+ CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT;
+ break;
+
+ case ElfSecurityLog:
+ CreateDisposition = FILE_OPEN_IF;
+ FileDesiredAccess = GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE;
+ SectionDesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE |
+ SECTION_QUERY | SECTION_EXTEND_SIZE;
+ SectionPageProtection = PAGE_READWRITE;
+ CreateOptions = FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT;
+ break;
+
+ case ElfBackupLog:
+ CreateDisposition = FILE_OPEN;
+ FileDesiredAccess = GENERIC_READ | SYNCHRONIZE;
+ SectionDesiredAccess = SECTION_MAP_READ | SECTION_QUERY;
+ SectionPageProtection = PAGE_READONLY;
+ CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT;
+ break;
+
+ }
+
+ ElfDbgPrint (("[ELF] Opening and mapping %ws\n",
+ pLogFile->LogFileName->Buffer));
+
+ if (pLogFile->FileHandle != NULL) {
+
+ //
+ // The log file is already in use. Do not reopen or remap it.
+ //
+
+ ElfDbgPrint(("[ELF] Log file already in use by another module\n"));
+
+ } else {
+
+ //
+ // Initialize the logfile structure so that it is easier to clean
+ // up.
+ //
+
+ pLogFile->ActualMaxFileSize = ELF_DEFAULT_LOG_SIZE;
+ pLogFile->Flags = 0;
+ pLogFile->BaseAddress = NULL;
+ pLogFile->SectionHandle = NULL;
+
+
+ //
+ // Set up the object attributes structure for the Log File
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ pLogFile->LogFileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Open the Log File. Create it if it does not exist and it's not
+ // being opened as a backup file. If creating, create a file of
+ // the maximum size configured.
+ //
+
+ MaximumSizeOfSection =
+ RtlConvertUlongToLargeInteger (ELF_DEFAULT_LOG_SIZE);
+
+ Status = NtCreateFile(
+ &pLogFile->FileHandle,
+ FileDesiredAccess,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ &MaximumSizeOfSection,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ CreateDisposition,
+ CreateOptions,
+ NULL,
+ 0);
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] Log File Open Failed 0x%lx\n", Status));
+ goto cleanup;
+ }
+
+ //
+ // If the file already existed, get its size and use that as the
+ // actual size of the file.
+ //
+
+ IoStatusInformation = IoStatusBlock.Information; // Save it away
+
+ if (!( IoStatusInformation & FILE_CREATED )) {
+ ElfDbgPrint (("[Elf] Log file exists.\n"));
+
+ Status = NtQueryInformationFile (
+ pLogFile->FileHandle,
+ &IoStatusBlock,
+ &FileStandardInfo,
+ sizeof (FileStandardInfo),
+ FileStandardInformation
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] QueryInformation failed 0x%lx\n", Status));
+ goto cleanup;
+ } else {
+
+
+ ElfDbgPrint(("[ELF] Use existing log file size: 0x%lx:%lx\n",
+ FileStandardInfo.EndOfFile.HighPart,
+ FileStandardInfo.EndOfFile.LowPart
+ ));
+
+ MaximumSizeOfSection.LowPart =
+ FileStandardInfo.EndOfFile.LowPart;
+ MaximumSizeOfSection.HighPart =
+ FileStandardInfo.EndOfFile.HighPart;
+
+ //
+ // Make sure that the high DWORD of the file size is ZERO.
+ //
+
+ ASSERT (MaximumSizeOfSection.HighPart == 0);
+
+ //
+ // If the filesize if 0, set it to the minimum size
+ //
+
+ if (MaximumSizeOfSection.LowPart == 0) {
+ MaximumSizeOfSection.LowPart = ELF_DEFAULT_LOG_SIZE;
+ }
+
+ //
+ // Set actual size of file
+ //
+
+ pLogFile->ActualMaxFileSize =
+ MaximumSizeOfSection.LowPart;
+
+ //
+ // If the size of the log file is reduced, a clear must
+ // happen for this to take effect
+ //
+
+ if (pLogFile->ActualMaxFileSize > pLogFile->ConfigMaxFileSize) {
+ pLogFile->ConfigMaxFileSize = pLogFile->ActualMaxFileSize;
+ }
+
+ }
+ }
+
+ //
+ // Create a section mapped to the Log File just opened
+ //
+
+ Status = NtCreateSection(
+ &pLogFile->SectionHandle,
+ SectionDesiredAccess,
+ NULL,
+ &MaximumSizeOfSection,
+ SectionPageProtection,
+ SEC_COMMIT,
+ pLogFile->FileHandle
+ );
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF] Log Mem Section Create Failed 0x%lx\n",
+ Status));
+ goto cleanup;
+ }
+
+ //
+ // Map a view of the Section into the eventlog address space
+ //
+
+ pLogFile->ViewSize = 0; // Initialize value to zero
+
+ Status = NtMapViewOfSection(
+ pLogFile->SectionHandle,
+ NtCurrentProcess(),
+ &pLogFile->BaseAddress,
+ 0,
+ 0,
+ NULL,
+ &pLogFile->ViewSize,
+ ViewUnmap,
+ 0,
+ SectionPageProtection);
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF] Log Mem Sect Map View failed 0x%lx\n",
+ Status));
+ goto cleanup;
+ }
+
+ //
+ // If the file was just created, write out the file header.
+ //
+
+ if ( IoStatusInformation & FILE_CREATED ) {
+ ElfDbgPrint(("[ELF] File was created\n"));
+JustCreated:
+ FileHeaderBuf.MaxSize = pLogFile->ActualMaxFileSize;
+ FileHeaderBuf.Flags = 0;
+ FileHeaderBuf.Retention = pLogFile->Retention;
+
+ //
+ // Copy the header into the file
+ //
+
+ ByteOffset = RtlConvertUlongToLargeInteger ( 0 );
+ Status = NtWriteFile(
+ pLogFile->FileHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &FileHeaderBuf, // Buffer
+ FILEHEADERBUFSIZE, // Length
+ &ByteOffset, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrint(("[ELF]: Log file header write failed 0x%lx\n",
+ Status));
+ goto cleanup;
+ }
+
+ //
+ // Copy the "EOF" record right after the header
+ //
+
+ ByteOffset = RtlConvertUlongToLargeInteger ( FILEHEADERBUFSIZE );
+ Status = NtWriteFile(
+ pLogFile->FileHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &EOFRecord, // Buffer
+ ELFEOFRECORDSIZE, // Length
+ &ByteOffset, // Byteoffset
+ NULL); // Key
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrint(("[ELF]: Log file header write failed 0x%lx\n",
+ Status));
+ goto cleanup;
+ }
+ }
+
+ //
+ // Check to ensure that this is a valid log file. We look at the
+ // size of the header and the signature to see if they match, as
+ // well as checking the version number.
+ //
+
+ pLogFileHeader = (PELF_LOGFILE_HEADER)(pLogFile->BaseAddress);
+
+ if ( (pLogFileHeader->HeaderSize != FILEHEADERBUFSIZE)
+ ||(pLogFileHeader->EndHeaderSize != FILEHEADERBUFSIZE)
+ ||(pLogFileHeader->Signature != ELF_LOG_FILE_SIGNATURE)
+ ||(pLogFileHeader->MajorVersion != ELF_VERSION_MAJOR)
+ ||(pLogFileHeader->MinorVersion != ELF_VERSION_MINOR)) {
+
+ //
+ // This file is corrupt, reset it to an empty log, unless
+ // it's being opened as a backup file, if it is, fail the
+ // open
+ //
+
+ ElfDbgPrint(("[ELF] Invalid file header found\n"));
+
+ if (LogType == ElfBackupLog) {
+
+ Status = STATUS_EVENTLOG_FILE_CORRUPT;
+ goto cleanup;
+ }
+ else {
+ ElfpCreateQueuedAlert(ALERT_ELF_LogFileCorrupt, 1,
+ &pLogFile->LogModuleName->Buffer);
+ //
+ // Treat it like it was just created
+ //
+
+ goto JustCreated;
+ }
+ }
+ else {
+
+ //
+ // If the "dirty" bit is set in the file header, then we need to
+ // revalidate the BeginRecord and EndRecord fields since we did not
+ // get a chance to write them out before the system was rebooted.
+ // If the dirty bit is set and it's a backup file, just fail the
+ // open.
+ //
+
+ if (pLogFileHeader->Flags & ELF_LOGFILE_HEADER_DIRTY) {
+
+ if (LogType == ElfBackupLog) {
+
+ Status = STATUS_EVENTLOG_FILE_CORRUPT;
+ goto cleanup;
+ }
+ else {
+ Status = RevalidateLogHeader (pLogFileHeader, pLogFile);
+ }
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Set the beginning and end record positions in our
+ // data structure as well as the wrap flag if appropriate.
+ //
+
+ pLogFile->EndRecord = pLogFileHeader->EndOffset;
+ pLogFile->BeginRecord = pLogFileHeader->StartOffset;
+ if (pLogFileHeader->Flags & ELF_LOGFILE_HEADER_WRAP) {
+ pLogFile->Flags |= ELF_LOGFILE_HEADER_WRAP;
+ }
+
+ ElfDbgPrint(("[ELF] BeginRecord: 0x%lx EndRecord: 0x%lx \n",
+ pLogFile->BeginRecord, pLogFile->EndRecord));
+ }
+ else {
+
+ //
+ // Couldn't validate the file, treat it like it was just
+ // created (turn it into an empty file)
+ //
+
+ goto JustCreated;
+ }
+
+#ifdef CORRUPTED
+
+ //
+ // Just for debugging a log corruption problem
+ //
+
+ if (!VerifyLogIntegrity(pLogFile)) {
+ ElfDbgPrintNC(("[ELF] Integrity check failed in ElfOpenLogFile\n"));
+ }
+#endif // CORRUPTED
+
+ }
+
+ //
+ // Fill in the first and last record number values in the LogFile
+ // data structure.
+ //
+
+ pLogFile->CurrentRecordNumber = pLogFileHeader->CurrentRecordNumber;
+ pLogFile->OldestRecordNumber = pLogFileHeader->OldestRecordNumber;
+
+ }
+
+ return (Status);
+
+cleanup:
+
+ // Clean up anything that got allocated
+
+ if (pLogFile->ViewSize) {
+ NtUnmapViewOfSection(NtCurrentProcess(), pLogFile->BaseAddress);
+ }
+
+ if (pLogFile->SectionHandle) {
+ NtClose(pLogFile->SectionHandle);
+ }
+
+ if (pLogFile->FileHandle) {
+ NtClose (pLogFile->FileHandle);
+ }
+
+ return (Status);
+
+}
diff --git a/private/eventlog/server/logclear.c b/private/eventlog/server/logclear.c
new file mode 100644
index 000000000..a59d86748
--- /dev/null
+++ b/private/eventlog/server/logclear.c
@@ -0,0 +1,621 @@
+/*++
+
+Copyright (c) 1991-1994 Microsoft Corporation
+
+Module Name:
+
+ LOGCLEAR.C
+
+Abstract:
+
+ Contains functions used to log an event indicating who cleared the log.
+ This is only called after the security log has been cleared.
+
+Author:
+
+ Dan Lafferty (danl) 01-July-1994
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 01-July-1994 danl & robertre
+ Created - Rob supplied the code which I fitted into the eventlog.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <stdio.h>
+#include <msaudite.h>
+#include <eventp.h>
+#include <tstr.h>
+
+#define NUM_STRINGS 6
+
+//
+// Globals
+//
+ PUNICODE_STRING pGlobalComputerNameU = NULL;
+ PANSI_STRING pGlobalComputerNameA = NULL;
+
+//
+// LOCAL FUNCTION PROTOTYPES
+//
+BOOL
+GetUserInfo(
+ IN HANDLE Token,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *DomainName,
+ OUT LPWSTR *AuthenticationId,
+ OUT PSID *UserSid
+ );
+
+NTSTATUS
+ElfpGetComputerName (
+ IN LPSTR *ComputerNamePtr
+ );
+
+PUNICODE_STRING
+TmpGetComputerNameW (VOID);
+
+VOID
+w_GetComputerName ( );
+
+
+VOID
+ElfpGenerateLogClearedEvent(
+ IELF_HANDLE LogHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function generates an event indicating that the log was cleared.
+
+Arguments:
+
+ LogHandle - This is a handle to the log that the event is to be placed in.
+ It is intended that this function only be called when the SecurityLog
+ has been cleared. So it is expected the LogHandle will always be
+ a handle to the security log.
+
+Return Value:
+
+ NONE - Either it works or it doesn't. If it doesn't, there isn't much
+ we can do about it.
+
+--*/
+{
+ LPWSTR UserName=NULL;
+ LPWSTR DomainName=NULL;
+ LPWSTR AuthenticationId=NULL;
+ LPWSTR ClientUserName=NULL;
+ LPWSTR ClientDomainName=NULL;
+ LPWSTR ClientAuthenticationId=NULL;
+ PSID UserSid=NULL;
+ PSID ClientSid=NULL;
+ DWORD i;
+ BOOL Result;
+ HANDLE Token;
+ PUNICODE_STRING StringPtrArray[NUM_STRINGS];
+ UNICODE_STRING StringArray[NUM_STRINGS];
+ NTSTATUS Status;
+ LARGE_INTEGER Time;
+ ULONG EventTime;
+ PUNICODE_STRING pComputerNameU;
+
+ Result = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &Token );
+
+ if (!Result) {
+ ElfDbgPrint(("OpenProcessToken failed, error = %d",GetLastError()));
+ return;
+ }
+
+ Result = GetUserInfo(
+ Token,
+ &UserName,
+ &DomainName,
+ &AuthenticationId,
+ &UserSid );
+
+ CloseHandle( Token );
+
+ if (!Result) {
+ ElfDbgPrint(("1st GetUserInfo ret'd %d\n",GetLastError()));
+ return;
+ }
+
+ ElfDbgPrint(("\nGetUserInfo ret'd \nUserName = %ws, "
+ "\nDomainName = %ws, \nAuthenticationId = %ws\n",
+ UserName, DomainName, AuthenticationId ));
+
+ Result = OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, FALSE, &Token );
+
+ if (Result) {
+
+ Result = GetUserInfo(
+ Token,
+ &ClientUserName,
+ &ClientDomainName,
+ &ClientAuthenticationId,
+ &ClientSid );
+
+ CloseHandle( Token );
+
+ if (!Result) {
+ ElfDbgPrint(("2nd GetUserInfo ret'd %d\n",GetLastError()));
+ goto CleanExit;
+ }
+
+ } else {
+
+ //
+ // Either got access denied or we're not impersonating.
+ // Use dash strings either way.
+ //
+
+ ClientUserName = L"-";
+ ClientDomainName = L"-";
+ ClientAuthenticationId = L"-";
+ }
+
+ ElfDbgPrint(("\nGetUserInfo ret'd \nUserName = %ws, "
+ "\nDomainName = %ws, \nAuthenticationId = %ws\n",
+ ClientUserName, ClientDomainName, ClientAuthenticationId ));
+
+
+ RtlInitUnicodeString( &StringArray[0], UserName );
+ RtlInitUnicodeString( &StringArray[1], DomainName );
+ RtlInitUnicodeString( &StringArray[2], AuthenticationId );
+ RtlInitUnicodeString( &StringArray[3], ClientUserName );
+ RtlInitUnicodeString( &StringArray[4], ClientDomainName );
+ RtlInitUnicodeString( &StringArray[5], ClientAuthenticationId );
+
+ //
+ // Create an array of pointers to UNICODE_STRINGs.
+ //
+ for (i=0; i<NUM_STRINGS; i++) {
+ StringPtrArray[i] = &StringArray[i];
+ }
+
+ //
+ // Generate the time of the event. This is done on the client side
+ // since that is where the event occurred.
+ //
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(&Time,
+ &EventTime
+ );
+ //
+ // Generate the ComputerName of the client.
+ // We have to do this in the client side since this call may be
+ // remoted to another server and we would not necessarily have
+ // the computer name there.
+ //
+ pComputerNameU = TmpGetComputerNameW();
+
+ Status = ElfrReportEventW (
+ LogHandle, // Log Handle
+ EventTime, // Time
+ EVENTLOG_AUDIT_SUCCESS, // Event Type
+ (USHORT)SE_CATEGID_SYSTEM, // Event Category
+ SE_AUDITID_AUDIT_LOG_CLEARED, // EventID
+ NUM_STRINGS, // NumStrings
+ 0, // DataSize
+ pComputerNameU, // pComputerNameU
+ UserSid, // UserSid
+ StringPtrArray, // *Strings
+ NULL, // Data
+ 0, // Flags
+ NULL, // RecordNumber
+ NULL // TimeWritten
+ );
+
+CleanExit:
+ //
+ // We only come down this path if we know for sure that these
+ // first three have been allocated.
+ //
+ ElfpFreeBuffer(UserName);
+ ElfpFreeBuffer(DomainName);
+ ElfpFreeBuffer(AuthenticationId);
+
+ if (UserSid != NULL) {
+ ElfpFreeBuffer(UserSid);
+ }
+ if (ClientUserName != NULL) {
+ ElfpFreeBuffer(ClientUserName);
+ }
+ if (ClientDomainName != NULL) {
+ ElfpFreeBuffer(ClientDomainName);
+ }
+ if (ClientAuthenticationId != NULL) {
+ ElfpFreeBuffer(ClientAuthenticationId);
+ }
+ if (ClientSid != NULL) {
+ ElfpFreeBuffer(ClientSid);
+ }
+}
+
+BOOL
+GetUserInfo(
+ IN HANDLE Token,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *DomainName,
+ OUT LPWSTR *AuthenticationId,
+ OUT PSID *UserSid
+ )
+/*++
+
+Routine Description:
+
+ This function gathers information about the user identified with the
+ token.
+
+Arguments:
+ Token - This token identifies the entity for which we are gathering
+ information.
+ UserName - This is the entity's user name.
+ DomainName - This is the entity's domain name.
+ AuthenticationId - This is the entity's Authentication ID.
+ UserSid - This is the entity's SID.
+
+NOTE:
+ Memory is allocated by this routine for UserName, DomainName,
+ AuthenticationId, and UserSid. It is the caller's responsibility to
+ free this memory.
+
+Return Value:
+ TRUE - If the operation was successful. It is possible that
+ UserSid did not get allocated in the successful case. Therefore,
+ the caller should test prior to freeing.
+ FALSE - If unsuccessful. No memory is allocated in this case.
+
+
+--*/
+{
+ PTOKEN_USER Buffer=NULL;
+// WCHAR User[256];
+ LPWSTR Domain = NULL;
+ LPWSTR AccountName = NULL;
+ SID_NAME_USE Use;
+ BOOL Result;
+ DWORD RequiredLength;
+ DWORD AccountNameSize;
+ DWORD DomainNameSize;
+ TOKEN_STATISTICS Statistics;
+ WCHAR LogonIdString[256];
+ DWORD Status=ERROR_SUCCESS;
+
+
+ *UserSid = NULL;
+
+ Result = GetTokenInformation ( Token, TokenUser, NULL, 0, &RequiredLength );
+
+ if (!Result) {
+
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+
+ Buffer = ElfpAllocateBuffer((RequiredLength+1)*sizeof(WCHAR) );
+
+ Result = GetTokenInformation ( Token,
+ TokenUser,
+ Buffer,
+ RequiredLength,
+ &RequiredLength
+ );
+
+ if (!Result) {
+ ElfDbgPrint(("2nd GetTokenInformation failed, "
+ "error = %d\n",GetLastError()));
+ return( FALSE );
+ }
+
+ } else {
+
+ DbgPrint("1st GetTokenInformation failed, error = %d\n",GetLastError());
+ return( FALSE );
+ }
+ }
+
+
+ if (!Result) {
+ goto ErrorCleanup;
+ }
+
+ AccountNameSize = 0;
+ DomainNameSize = 0;
+
+ Result = LookupAccountSidW( L"",
+ Buffer->User.Sid,
+ NULL,
+ &AccountNameSize,
+ NULL,
+ &DomainNameSize,
+ &Use
+ );
+
+ if (!Result) {
+
+ if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
+
+ AccountName = ElfpAllocateBuffer((AccountNameSize+1)*sizeof(WCHAR) );
+ Domain = ElfpAllocateBuffer((DomainNameSize+1)*sizeof(WCHAR) );
+
+ if ( AccountName == NULL ) {
+ ElfDbgPrint(("LocalAlloc failed allocating %d bytes, "
+ "error = %d\n",AccountNameSize,GetLastError()));
+ goto ErrorCleanup;
+ }
+
+ if ( Domain == NULL ) {
+ ElfDbgPrint(("LocalAlloc failed allocating %d bytes, "
+ "error = %d\n",DomainNameSize,GetLastError()));
+ goto ErrorCleanup;
+ }
+
+ Result = LookupAccountSidW( L"",
+ Buffer->User.Sid,
+ AccountName,
+ &AccountNameSize,
+ Domain,
+ &DomainNameSize,
+ &Use
+ );
+ if (!Result) {
+
+ ElfDbgPrint(("2nd LookupAccountSid failed, "
+ "error = %d\n",GetLastError()));
+ goto ErrorCleanup;
+ }
+
+ } else {
+
+ ElfDbgPrint(("1st LookupAccountSid failed, "
+ "error = %d\n",GetLastError()));
+ goto ErrorCleanup;
+ }
+
+ } else {
+
+ ElfDbgPrint(("LookupAccountSid succeeded unexpectedly\n"));
+ goto ErrorCleanup;
+ }
+
+ ElfDbgPrint(("Name = %ws\\%ws\n",Domain,AccountName));
+
+ Result = GetTokenInformation ( Token, TokenStatistics, &Statistics, sizeof( Statistics ), &RequiredLength );
+
+ if (!Result) {
+ ElfDbgPrint(("GetTokenInformation failed, error = %d\n",GetLastError()));
+ goto ErrorCleanup;
+ }
+
+ swprintf(LogonIdString, L"(0x%X,0x%X)",Statistics.AuthenticationId.HighPart, Statistics.AuthenticationId.LowPart );
+ ElfDbgPrint(("LogonIdString = %ws\n",LogonIdString));
+
+ *AuthenticationId = ElfpAllocateBuffer(WCSSIZE(LogonIdString));
+ if (*AuthenticationId == NULL) {
+ ElfDbgPrint(("[ELF]GetUserInfo: Failed to allocate buffer "
+ "for AuthenticationId %d\n",GetLastError()));
+ goto ErrorCleanup;
+ }
+ wcscpy(*AuthenticationId, LogonIdString);
+
+ //
+ // Return accumulated information
+ //
+
+ *UserSid = ElfpAllocateBuffer(GetLengthSid( Buffer->User.Sid ) );
+
+ Result = CopySid( GetLengthSid( Buffer->User.Sid ), *UserSid, Buffer->User.Sid );
+
+ ElfpFreeBuffer(Buffer);
+
+ *DomainName = Domain;
+ *UserName = AccountName;
+
+ return( TRUE );
+
+ErrorCleanup:
+
+ if (Buffer != NULL) {
+ ElfpFreeBuffer( Buffer );
+ }
+
+ if (Domain != NULL) {
+ ElfpFreeBuffer( Domain );
+ }
+
+ if (AccountName != NULL) {
+ ElfpFreeBuffer( AccountName );
+ }
+
+ if (*UserSid != NULL) {
+ ElfpFreeBuffer( *UserSid );
+ }
+
+ if (*AuthenticationId != NULL) {
+ ElfpFreeBuffer( *AuthenticationId );
+ }
+
+ return( FALSE );
+}
+
+NTSTATUS
+ElfpGetComputerName (
+ IN LPSTR *ComputerNamePtr)
+
+/*++
+
+Routine Description:
+
+ This routine obtains the computer name from a persistent database,
+ by calling the GetcomputerNameA Win32 Base API
+
+ This routine assumes the length of the computername is no greater
+ than MAX_COMPUTERNAME_LENGTH, space for which it allocates using
+ LocalAlloc. It is necessary for the user to free that space using
+ ElfpFreeBuffer when finished.
+
+Arguments:
+
+ ComputerNamePtr - This is a pointer to the location where the pointer
+ to the computer name is to be placed.
+
+Return Value:
+
+ NERR_Success - If the operation was successful.
+
+ It will return assorted Net or Win32 or NT error messages if not.
+
+--*/
+{
+ DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1;
+
+ //
+ // Allocate a buffer to hold the largest possible computer name.
+ //
+
+ *ComputerNamePtr = ElfpAllocateBuffer(nSize);
+
+ if (*ComputerNamePtr == NULL) {
+ return (GetLastError());
+ }
+
+ //
+ // Get the computer name string into the locally allocated buffer
+ // by calling the Win32 GetComputerNameA API.
+ //
+
+ if (!GetComputerNameA(*ComputerNamePtr, &nSize)) {
+ ElfpFreeBuffer(*ComputerNamePtr);
+ *ComputerNamePtr = NULL;
+ return (GetLastError());
+ }
+
+ return (ERROR_SUCCESS);
+}
+
+VOID
+w_GetComputerName ( )
+
+/*++
+
+Routine Description:
+
+ This routine gets the name of the computer. It checks the global
+ variable to see if the computer name has already been determined.
+ If not, it updates that variable with the name.
+ It does this for the UNICODE and the ANSI versions.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+
+--*/
+{
+ PUNICODE_STRING pNameU=NULL;
+ PANSI_STRING pNameA=NULL;
+ LPSTR pName;
+ NTSTATUS Error;
+ NTSTATUS Status;
+
+
+ if (pGlobalComputerNameU != NULL) {
+ return;
+ }
+ pNameU = ElfpAllocateBuffer (sizeof (UNICODE_STRING));
+ pNameA = ElfpAllocateBuffer (sizeof (ANSI_STRING));
+
+ if ((pNameU != NULL) && (pNameA != NULL)) {
+
+ if ((Error = ElfpGetComputerName (&pName)) == ERROR_SUCCESS) {
+
+ //
+ // ElfpComputerName has allocated a buffer to contain the
+ // ASCII name of the computer. We use that for the ANSI
+ // string structure.
+ //
+ RtlInitAnsiString ( pNameA, pName );
+
+ } else {
+ //
+ // We could not get the computer name for some reason. Set up
+ // the golbal pointer to point to the NULL string.
+ //
+ RtlInitAnsiString ( pNameA, "\0");
+ }
+
+ //
+ // Set up the UNICODE_STRING structure.
+ //
+ Status = RtlAnsiStringToUnicodeString (
+ pNameU,
+ pNameA,
+ TRUE
+ );
+
+ //
+ // If there was no error, set the global variables.
+ // Otherwise, free the buffer allocated by ElfpGetComputerName
+ // and leave the global variables unchanged.
+ //
+ if (NT_SUCCESS(Status)) {
+
+ pGlobalComputerNameU = pNameU; // Set global variable if no error
+ pGlobalComputerNameA = pNameA; // Set global ANSI variable
+
+ } else {
+
+ ElfDbgPrint(("[ELFCLNT] GetComputerName - Error 0x%lx\n", Status));
+ ElfpFreeBuffer(pName);
+ ElfpFreeBuffer (pNameU); // Free the buffers
+ ElfpFreeBuffer (pNameA);
+ }
+
+ }
+}
+
+
+PUNICODE_STRING
+TmpGetComputerNameW ( )
+
+/*++
+
+Routine Description:
+
+ This routine gets the UNICODE name of the computer. It checks the global
+ variable to see if the computer name has already been determined.
+ If not, it calls the worker routine to do that.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ Returns a pointer to the computer name, or a NULL.
+
+
+--*/
+{
+ if (pGlobalComputerNameU == NULL) {
+ w_GetComputerName();
+ }
+ return (pGlobalComputerNameU);
+}
+
+
+
diff --git a/private/eventlog/server/memory.c b/private/eventlog/server/memory.c
new file mode 100644
index 000000000..8510786e1
--- /dev/null
+++ b/private/eventlog/server/memory.c
@@ -0,0 +1,149 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ MEMORY.C
+
+Abstract:
+
+ This file contains the routines that deal with memory management.
+
+Author:
+
+ Rajen Shah (rajens) 12-Jul-1991
+
+[Environment:]
+
+ User Mode - Win32, except for NTSTATUS returned by some functions.
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+
+//
+// Implement my own tail-checking, since the system code requires a
+// debugging build
+//
+
+//#define TAIL_CHECKING
+#ifdef TAIL_CHECKING
+#define CHECK_HEAP_TAIL_SIZE 16
+#define CHECK_HEAP_TAIL_FILL 0xAB
+#endif
+
+
+PVOID
+ElfpAllocateBuffer (
+ ULONG Size
+ )
+
+/*++
+
+Routine Description:
+
+ Allocate a buffer of the given size, and return the pointer in BufPtr.
+
+
+Arguments:
+
+
+
+Return Value:
+
+ Pointer to allocated buffer (or NULL).
+
+Note:
+
+
+--*/
+{
+ PVOID BufPtr;
+
+#ifdef TAIL_CHECKING
+ //
+ // Keep the offset of the pattern (so we don't have to have internal
+ // knowledge about the granularity of the heap block) and copy a
+ // known pattern after the end of the user's block
+ //
+ BufPtr = (PVOID *) MIDL_user_allocate ( Size
+ + CHECK_HEAP_TAIL_SIZE + sizeof(DWORD));
+ *((PDWORD)BufPtr) = Size + sizeof(DWORD);
+ (PBYTE) BufPtr += sizeof(DWORD);
+ RtlFillMemory((PBYTE)BufPtr + Size,
+ CHECK_HEAP_TAIL_SIZE,
+ CHECK_HEAP_TAIL_FILL);
+#else
+
+ BufPtr = (PVOID *) MIDL_user_allocate ( Size );
+
+#endif
+
+ return (BufPtr);
+
+}
+
+
+
+VOID
+ElfpFreeBuffer (
+ PVOID BufPtr)
+
+/*++
+
+Routine Description:
+
+ Frees a buffer previously allocated by AllocateBuffer.
+
+
+Arguments:
+
+ Pointer to buffer.
+
+Return Value:
+
+ NOTHING
+
+Note:
+
+
+--*/
+{
+
+#ifdef TAIL_CHECKING
+ {
+ DWORD i;
+ PBYTE pb;
+
+ //
+ // Back up to real start of block
+ //
+
+ (PBYTE)BufPtr -= sizeof(DWORD);
+ i = *((PDWORD)BufPtr);
+ pb = (PBYTE)BufPtr + i;
+
+ for (i = 0; i < CHECK_HEAP_TAIL_SIZE ; i++, pb++) {
+ if (*pb != CHECK_HEAP_TAIL_FILL) {
+ ElfDbgPrint(("[ELF] Heap has been corrupted at 0x%x\n",
+ BufPtr));
+ // Make it access violate
+ pb = (PBYTE) 0;
+ *pb = 1;
+ }
+ }
+ }
+#endif
+
+ MIDL_user_free ( BufPtr );
+ return;
+
+}
diff --git a/private/eventlog/server/operate.c b/private/eventlog/server/operate.c
new file mode 100644
index 000000000..d8e9404c6
--- /dev/null
+++ b/private/eventlog/server/operate.c
@@ -0,0 +1,3117 @@
+/*++
+
+Copyright (c) 1990-1994 Microsoft Corporation
+
+Module Name:
+
+ OPERATE.C
+
+Abstract:
+
+ This file contains all the routines to perform operations on the
+ log files. They are called by the thread that performs requests.
+
+Author:
+
+ Rajen Shah (rajens) 16-Jul-1991
+
+Revision History:
+
+ 04-Apr-1995 MarkBl
+ Resets the file archive attribute on log write. The backup caller
+ clears it.
+ 29-Aug-1994 Danl
+ We no longer grow log files in place. Therefore, the ExtendSize
+ function will allocate a block that is as large as the old size plus
+ the size of the new block that must be added. If this allocation
+ succeeds, then it will free up the old block. If a failure occurs,
+ we continue to use the old block as if we have already grown as much
+ as we can.
+ 22-Jul-1994 Danl
+ ValidFilePos: Changed test for pEndRecordLen > PhysicalEOF
+ so that it uses >=. In the case where pEndRecordLen == PhysicalEOF,
+ we want to wrap to find the last DWORD at the beginning of the File
+ (after the header).
+
+ 08-Jul-1994 Danl
+ PerformWriteRequest: Fixed overwrite logic so that in the case where
+ a log is set up for always-overwrite, that we never go down the branch
+ that indicates the log was full. Previously, it would go down that
+ branch if the current time was less than the log time (ie. someone
+ set the clock back).
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <alertmsg.h> // ALERT_ELF manifests
+
+#define OVERWRITE_AS_NEEDED 0x00000000
+#define NEVER_OVERWRITE 0xffffffff
+
+#if DBG
+#define ELF_ERR_OUT(txt,code,pLogFile) (ElfErrorOut(txt,code,pLogFile))
+#else
+#define ELF_ERR_OUT(txt,code,pLogFile)
+#endif
+
+//
+// Prototypes
+//
+BOOL
+IsPositionWithinRange(
+ PVOID Position,
+ PVOID BeginningRecord,
+ PVOID EndingRecord);
+
+#if DBG
+VOID
+ElfErrorOut(
+ LPSTR ErrorText,
+ DWORD StatusCode,
+ PLOGFILE pLogFile);
+#endif // DBG
+
+
+VOID
+ElfExtendFile (
+ PLOGFILE pLogFile,
+ ULONG SpaceNeeded,
+ PULONG SpaceAvail
+ )
+/*++
+
+Routine Description:
+
+ This routine takes an open log file and extends the file and underlying
+ section and view if possible. If it can't be grown, it caps the file
+ at this size by setting the ConfigMaxFileSize to the Actual. It also
+ updates the SpaceAvail parm which is used in PerformWriteRequest (the
+ caller).
+
+Arguments:
+
+ pLogFile - pointer to a LOGFILE structure for the open logfile
+ ExtendAmount - How much bigger to make the file/section/view
+ SpaceAvail - Update this with how much space was added to the section
+
+Return Value:
+
+ None - If we can't extend the file, we just cap it at this size for the
+ duration of this boot. We'll try again the next time the eventlog
+ is closed and reopened.
+
+Note:
+
+ ExtendAmount should always be granular to 64K.
+
+--*/
+{
+
+ LARGE_INTEGER NewSize;
+ NTSTATUS Status;
+ PVOID BaseAddress;
+ ULONG Size;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ //
+ // Calculate how much to grow the file then extend the section by
+ // that amount. Do this in 64K chunks.
+ //
+
+ SpaceNeeded = ((SpaceNeeded - *SpaceAvail) & 0xFFFF0000) + 0x10000;
+
+ if (SpaceNeeded > (pLogFile->ConfigMaxFileSize - pLogFile->ActualMaxFileSize))
+ {
+
+ //
+ // We can't grow it by the full amount we need. Grow
+ // it to the max size and let file wrap take over.
+ // If there isn't any room to grow, then return;
+ //
+
+ SpaceNeeded = pLogFile->ConfigMaxFileSize -
+ pLogFile->ActualMaxFileSize;
+
+ if (SpaceNeeded == 0) {
+ return;
+ }
+ }
+
+ NewSize = RtlConvertUlongToLargeInteger (pLogFile->ActualMaxFileSize
+ + SpaceNeeded);
+
+ //
+ // Update the file size information, extend the section, and map the
+ // new section
+ //
+
+ Status = NtSetInformationFile(pLogFile->FileHandle,
+ &IoStatusBlock,
+ &NewSize,
+ sizeof(NewSize),
+ FileEndOfFileInformation);
+
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrintNC(("[EVENTLOG]ElfExtendFile: NtSetInformationFile "
+ "failed 0x%lx\n",Status));
+ goto ErrorExit;
+ }
+
+ Status = NtExtendSection(pLogFile->SectionHandle, &NewSize);
+
+ if (!NT_SUCCESS(Status)) {
+ goto ErrorExit;
+ }
+
+ //
+ // Now that the section is extended, we need to map the new section.
+ //
+
+ //
+ // Map a view of the entire section (with the extension).
+ // Allow the allocator to tell us where it is located, and
+ // what the size is.
+ //
+ BaseAddress = NULL;
+ Size = 0;
+ Status = NtMapViewOfSection(
+ pLogFile->SectionHandle,
+ NtCurrentProcess(),
+ &BaseAddress,
+ 0,
+ 0,
+ NULL,
+ &Size,
+ ViewUnmap,
+ 0,
+ PAGE_READWRITE);
+
+ if (!NT_SUCCESS(Status)) {
+ //
+ // If this fails, just exit, and we will continue with the
+ // view that we have.
+ //
+ ELF_ERR_OUT("ElfExtendFile:NtMapViewOfSection Failed",
+ Status, pLogFile);
+ goto ErrorExit;
+ }
+
+ //
+ // Unmap the old section.
+ //
+
+ Status = NtUnmapViewOfSection(
+ NtCurrentProcess(),
+ pLogFile->BaseAddress);
+
+ if (!NT_SUCCESS(Status)) {
+ ELF_ERR_OUT("ElfExtendFile:NtUnmapViewOfSection Failed",
+ Status, pLogFile);
+ }
+ pLogFile->BaseAddress = BaseAddress;
+
+ //
+ // We managed to extend the file, update the actual size
+ // and space available and press on.
+ //
+
+ pLogFile->ViewSize += SpaceNeeded;
+ pLogFile->ActualMaxFileSize += SpaceNeeded;
+ *SpaceAvail += SpaceNeeded;
+
+ //
+ // If we were wrapped, turn off the bit since now we're in a state
+ // that is unwrapped, except there may be some dead space after the
+ // header, but before the start of the first full record. This will
+ // be reclaimed when the file really wraps again.
+ //
+
+ pLogFile->Flags &= ~ELF_LOGFILE_HEADER_WRAP;
+
+ //
+ // Now flush this to disk to commit it
+ //
+
+ BaseAddress = pLogFile->BaseAddress;
+ Size = FILEHEADERBUFSIZE;
+
+ Status = NtFlushVirtualMemory(
+ NtCurrentProcess(),
+ &BaseAddress,
+ &Size,
+ &IoStatusBlock
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ ELF_ERR_OUT("ElfExtendFile:NtFlushVirtualMemory Failed",
+ Status, pLogFile);
+ }
+ return;
+
+ErrorExit:
+ //
+ // Couldn't extend the section for some reason. Just wrap the file now.
+ // Cap the file at this size, so we don't try and extend the section on
+ // every write. The next time the eventlog service is started up it
+ // will revert to the configured max again.
+ //
+ // Generate an Alert here - BUGBUG
+ //
+ ELF_ERR_OUT("ElfExtendFile:Couldn't extend the File",
+ Status, pLogFile);
+
+ pLogFile->ConfigMaxFileSize = pLogFile->ActualMaxFileSize;
+
+ return;
+
+}
+
+
+NTSTATUS
+CopyUnicodeToAnsiRecord (
+ PVOID Dest,
+ PVOID Src,
+ PVOID *NewBufPos,
+ PULONG RecordSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reads from the event log specified in the request packet.
+
+ This routine uses memory mapped I/O to access the log file. This makes
+ it much easier to move around the file.
+
+Arguments:
+
+ Dest - Points to destination buffer.
+ Src - Points to the UNICODE record.
+ NewBufPos - Gets offset in Dest buffer after record just transferred.
+ RecordSize - Size of this (ANSI) record.
+
+Return Value:
+
+ Actual size of record copied into the Dest buffer.
+
+Note:
+
+--*/
+{
+ ANSI_STRING StringA;
+ UNICODE_STRING StringU;
+ PEVENTLOGRECORD SrcRecord, DestRecord;
+ PWSTR pStringU;
+ PVOID TempPtr;
+ ULONG PadSize, i;
+ ULONG zero = 0;
+ WCHAR *SrcStrings, *DestStrings;
+ ULONG RecordLength, *pLength;
+ NTSTATUS Status;
+
+ DestRecord = (PEVENTLOGRECORD)Dest;
+ SrcRecord = (PEVENTLOGRECORD)Src;
+
+ DestRecord->TimeGenerated = SrcRecord->TimeGenerated;
+ DestRecord->Reserved = SrcRecord->Reserved;
+ DestRecord->RecordNumber = SrcRecord->RecordNumber;
+ DestRecord->TimeWritten = SrcRecord->TimeWritten;
+ DestRecord->EventID = SrcRecord->EventID;
+ DestRecord->EventType = SrcRecord->EventType;
+ DestRecord->EventCategory = SrcRecord->EventCategory;
+ DestRecord->NumStrings = SrcRecord->NumStrings;
+ DestRecord->UserSidLength = SrcRecord->UserSidLength;
+ DestRecord->DataLength = SrcRecord->DataLength;
+
+ //
+ // Convert and copy over modulename
+ //
+ pStringU = (PWSTR)((ULONG)SrcRecord + sizeof(EVENTLOGRECORD));
+
+ RtlInitUnicodeString ( &StringU, pStringU );
+
+ Status = RtlUnicodeStringToAnsiString (
+ &StringA,
+ &StringU,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ TempPtr = (PVOID)((ULONG)DestRecord + sizeof(EVENTLOGRECORD));
+
+ RtlMoveMemory ( TempPtr, StringA.Buffer, StringA.MaximumLength );
+
+ TempPtr = (PVOID)((ULONG) TempPtr + StringA.MaximumLength);
+
+ RtlFreeAnsiString(&StringA);
+
+ //
+ // Convert and copy over computername
+ //
+ // TempPtr points to location in the destination for the computername
+ //
+
+ pStringU = (PWSTR)((ULONG)pStringU + StringU.MaximumLength);
+
+ RtlInitUnicodeString ( &StringU, pStringU );
+
+ Status = RtlUnicodeStringToAnsiString (
+ &StringA,
+ &StringU,
+ TRUE
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ RtlMoveMemory ( TempPtr, StringA.Buffer, StringA.MaximumLength );
+
+ TempPtr = (PVOID)((ULONG) TempPtr + StringA.MaximumLength);
+
+ RtlFreeAnsiString(&StringA);
+
+ }
+ }
+ if (NT_SUCCESS(Status)) {
+
+ // TempPtr points to location after computername - i.e. UserSid.
+ // Before we write out the UserSid, we ensure that we pad the
+ // bytes so that the UserSid starts on a DWORD boundary.
+ //
+ PadSize = sizeof(ULONG) - (((ULONG)TempPtr-(ULONG)DestRecord) % sizeof(ULONG));
+
+ RtlMoveMemory (
+ TempPtr,
+ &zero,
+ PadSize
+ );
+
+ TempPtr = (PVOID)((ULONG)TempPtr + PadSize);
+ //
+ // Copy over the UserSid.
+ //
+ RtlMoveMemory (
+ TempPtr,
+ (PVOID)((ULONG)SrcRecord + SrcRecord->UserSidOffset),
+ SrcRecord->UserSidLength
+ );
+
+ DestRecord->UserSidOffset = (ULONG)( (ULONG)TempPtr
+ - (ULONG)DestRecord);
+ //
+ // Copy over the Strings
+ //
+ TempPtr = (PVOID)((ULONG)TempPtr + SrcRecord->UserSidLength);
+ SrcStrings = (WCHAR *) ((ULONG)SrcRecord + (ULONG)SrcRecord->StringOffset);
+ DestStrings = (WCHAR *)TempPtr;
+
+ for (i=0; i < DestRecord->NumStrings; i++) {
+
+ RtlInitUnicodeString (&StringU, SrcStrings);
+
+ RtlUnicodeStringToAnsiString (
+ &StringA,
+ &StringU,
+ TRUE
+ );
+
+ RtlMoveMemory (
+ DestStrings,
+ StringA.Buffer,
+ StringA.MaximumLength
+ );
+
+ DestStrings = (WCHAR*)( (ULONG)DestStrings
+ + (ULONG)StringA.MaximumLength);
+
+ SrcStrings = (WCHAR*)( (ULONG)SrcStrings
+ + (ULONG)StringU.MaximumLength);
+
+ RtlFreeAnsiString (&StringA);
+ }
+
+ DestRecord->StringOffset = (ULONG)TempPtr - (ULONG)DestRecord;
+
+ //
+ // Copy over the binary Data
+ //
+ // DestStrings points to the point after the last string copied.
+ //
+ TempPtr = (PVOID)DestStrings;
+ DestRecord->DataOffset = (ULONG)TempPtr - (ULONG)DestRecord;
+
+ RtlMoveMemory (
+ TempPtr,
+ (PVOID)((ULONG)SrcRecord + SrcRecord->DataOffset),
+ SrcRecord->DataLength
+ );
+
+ //
+ // Now do the pad bytes.
+ //
+ TempPtr = (PVOID)((ULONG)TempPtr + SrcRecord->DataLength);
+ PadSize = sizeof(ULONG) - (((ULONG)TempPtr-(ULONG)DestRecord) % sizeof(ULONG));
+
+ RtlMoveMemory (
+ TempPtr,
+ &zero,
+ PadSize
+ );
+
+ RecordLength = (ULONG)TempPtr + PadSize + sizeof(ULONG) - (ULONG)DestRecord;
+
+ pLength = (PULONG)((ULONG)TempPtr + PadSize);
+ *pLength = RecordLength;
+ DestRecord->Length = RecordLength;
+ ASSERT (((ULONG)DestRecord + RecordLength) == ((ULONG)pLength + sizeof(ULONG)));
+
+ //
+ // Set up return information
+ //
+ *NewBufPos = (PVOID)((ULONG)DestRecord + RecordLength);
+ *RecordSize = RecordLength;
+
+ }
+ return (Status);
+} // CopyUnicodeToAnsiRecord
+
+BOOL
+ValidFilePos (
+ PVOID Position,
+ PVOID BeginningRecord,
+ PVOID EndingRecord,
+ PVOID PhysicalEOF,
+ PVOID BaseAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This routine determines whether we are pointing to a valid beginning
+ of an event record in the event log. It does this by validating
+ the signature then comparing the length at the beginning of the record to
+ the length at the end, both of which have to be at least the size of the
+ fixed length portion of an eventlog record.
+
+Arguments:
+
+ Position - Pointer to be verified.
+ BeginningRecord - Pointer to the beginning record in the file.
+ EndingRecord - Pointer to the byte after the ending record in the file.
+ PhysicalEOF - Pointer the physical end of the log.
+ BaseAddress - Pointer to the physical beginning of the log.
+
+Return Value:
+
+ TRUE if this position is valid.
+
+Note:
+
+ There is a probability of error if a record just happens to have the
+ ULONG at the current position the same as the value that number of
+ bytes further on in the record. However, this is a very slim chance.
+
+
+--*/
+{
+ PULONG pEndRecordLength;
+ BOOL fValid = FALSE;
+ PEVENTLOGRECORD pEventRecord;
+
+ try {
+ pEventRecord = (PEVENTLOGRECORD)Position;
+
+ //
+ // Verify that the pointer is within the range of BEGINNING->END
+ //
+
+ fValid = IsPositionWithinRange(Position,
+ BeginningRecord,
+ EndingRecord);
+
+ //
+ // If the offset looks OK, then examine the lengths at the beginning
+ // and end of the current record. If they don't match, then the position
+ // is invalid.
+ //
+
+ if (fValid) {
+
+ //
+ // Make sure the length is a multiple number of DWORDS
+ //
+
+ if (pEventRecord->Length & 3) {
+ fValid = FALSE;
+ }
+ else {
+ pEndRecordLength =
+ (PULONG) ((PBYTE)Position + pEventRecord->Length) - 1;
+
+ //
+ // If the file is wrapped, adjust the pointer to reflect the
+ // portion of the record that is wrapped starting after the header
+ //
+
+ if ((PVOID) pEndRecordLength >= PhysicalEOF) {
+ pEndRecordLength = (PULONG) ((PBYTE) BaseAddress +
+ ((PBYTE) pEndRecordLength - (PBYTE) PhysicalEOF) +
+ FILEHEADERBUFSIZE);
+ }
+
+ if (pEventRecord->Length == *pEndRecordLength &&
+ (pEventRecord->Length == ELFEOFRECORDSIZE ||
+ (pEventRecord->Length >= sizeof(EVENTLOGRECORD) &&
+ pEventRecord->Reserved == ELF_LOG_FILE_SIGNATURE))
+ ) {
+
+ fValid = TRUE;
+
+ } else {
+ fValid = FALSE;
+ }
+ }
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ fValid = FALSE;
+ }
+
+ return(fValid);
+}
+
+
+BOOL
+IsPositionWithinRange(
+ PVOID Position,
+ PVOID BeginningRecord,
+ PVOID EndingRecord)
+{
+ //
+ // Verify that the pointer is within the range of BEGINNING->END
+ //
+
+ if (EndingRecord > BeginningRecord) {
+ if ((Position >= BeginningRecord) && (Position <= EndingRecord))
+ return(TRUE);
+
+ } else if (EndingRecord < BeginningRecord) {
+ if ((Position >= BeginningRecord) || (Position <= EndingRecord))
+ return(TRUE);
+
+ } else {
+ return(FALSE);
+ }
+}
+
+
+PVOID
+FindStartOfNextRecord (
+ PVOID Position,
+ PVOID BeginningRecord,
+ PVOID EndingRecord,
+ PVOID PhysicalStart,
+ PVOID PhysicalEOF,
+ PVOID BaseAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This routine starts at Position, and finds the beginning of the next
+ valid record, wrapping around the physical end of the file if necessary.
+
+Arguments:
+
+ Position - Pointer at which to start the search.
+ BeginningRecord - Pointer to the beginning record in the file.
+ EndingRecord - Pointer to the byte after the ending record in the file.
+ PhysicalStart - Pointer to the start of log infor (after header)
+ PhysicalEOF - Pointer the physical end of the log.
+ BaseAddress - Pointer to the physical beginning of the log.
+
+Return Value:
+
+ A pointer to the start of the next valid record. NULL if there is no
+ valid record.
+
+Note:
+
+ There is a probability of error if a record just happens to have the
+ ULONG at the current position the same as the value that number of
+ bytes further on in the record. However, this is a very slim chance.
+
+
+--*/
+{
+
+ PULONG ptr;
+ PULONG EndOfBlock;
+ PULONG EndOfFile;
+ PVOID pRecord;
+ ULONG Size;
+ BOOL StillLooking = TRUE;
+
+ //
+ // Search for a ULONG which matches a record signature
+ //
+
+ ptr = (PULONG) Position;
+ EndOfBlock = EndOfFile = (PULONG) PhysicalEOF - 1;
+
+ while (StillLooking) {
+
+ //
+ // Check to see if it is the EOF record
+ //
+
+ if (*ptr == ELFEOFRECORDSIZE) {
+
+ //
+ // Only scan up to the end of the file. Just compare up the
+ // constant information
+ //
+
+ Size = min (ELFEOFUNIQUEPART,
+ (PBYTE) PhysicalEOF - (PBYTE) ptr);
+
+ pRecord = (PVOID) CONTAINING_RECORD(ptr, ELF_EOF_RECORD,
+ RecordSizeBeginning);
+
+ if (RtlCompareMemory (
+ pRecord,
+ &EOFRecord,
+ Size) == Size) {
+ //
+ // This is the EOF record, back up to the last record
+ //
+
+ (PBYTE) pRecord -= *((PULONG) pRecord - 1);
+ if (pRecord < PhysicalStart) {
+ pRecord = (PVOID) ((PBYTE) PhysicalEOF -
+ ((PBYTE)PhysicalStart - (PBYTE)pRecord));
+ }
+
+ }
+
+ if (ValidFilePos(pRecord, BeginningRecord, EndingRecord,
+ PhysicalEOF, BaseAddress)) {
+ return(pRecord);
+ }
+ }
+
+ //
+ // Check to see if it is an event record
+ //
+
+ if (*ptr == ELF_LOG_FILE_SIGNATURE) {
+
+ //
+ // This is a signature, see if the containing record is valid
+ //
+
+ pRecord = (PVOID) CONTAINING_RECORD(ptr, EVENTLOGRECORD,
+ Reserved);
+ if (ValidFilePos(pRecord, BeginningRecord, EndingRecord,
+ PhysicalEOF, BaseAddress)) {
+ return(pRecord);
+ }
+
+ }
+
+ //
+ // Bump to the next byte and see if we're done.
+ //
+
+ ptr++;
+
+ if (ptr >= EndOfBlock) {
+
+ //
+ // Need the second test on this condition in case Position
+ // happens to equal PhysicalEOF - 1 (EndOfBlock initial value);
+ // without this, this loop would terminate prematurely.
+ //
+
+ if ((EndOfBlock == (PULONG) Position) &&
+ ((PULONG) Position != EndOfFile)) {
+
+ //
+ // This was the top half, so we're done
+ //
+
+ StillLooking = FALSE;
+ }
+ else {
+
+ //
+ // This was the bottom half, let's look in the top half
+ //
+
+ EndOfBlock = (PULONG) Position;
+ ptr = (PULONG) PhysicalStart;
+ }
+ }
+ }
+
+
+
+ //
+ // Didn't find a valid record
+ //
+
+ return(NULL);
+
+}
+
+PVOID
+NextRecordPosition (
+ ULONG ReadFlags,
+ PVOID CurrPosition,
+ ULONG CurrRecordLength,
+ PVOID BeginRecord,
+ PVOID EndRecord,
+ PVOID PhysicalEOF,
+ PVOID PhysStart
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine seeks to the beginning of the next record to be read
+ depending on the flags in the request packet.
+
+Arguments:
+
+ ReadFlags - Read forwards or backwards
+ CurrPosition - Pointer to the current position.
+ CurrRecordLength - Length of the record at the last position read.
+ BeginRecord - Logical first record
+ EndRecord - Logical last record (EOF record)
+ PhysEOF - End of file
+ PhysStart - Start of file pointer (following file header).
+
+Return Value:
+
+ New position or NULL if invalid record.
+
+Note:
+
+
+--*/
+{
+
+ PVOID NewPosition;
+ ULONG Length;
+ PDWORD FillDword;
+
+ if (ReadFlags & EVENTLOG_FORWARDS_READ) {
+
+ //
+ // If we're pointing at the EOF record, just set the position to
+ // the first record
+ //
+
+ if (CurrRecordLength == ELFEOFRECORDSIZE) {
+ return(BeginRecord);
+ }
+
+ NewPosition = (PVOID) ((ULONG)CurrPosition + CurrRecordLength);
+
+ //
+ // Take care of wrapping.
+ //
+
+ if (NewPosition >= PhysicalEOF) {
+ NewPosition = (PVOID)((PBYTE)PhysStart +
+ ((PBYTE) NewPosition - (PBYTE) PhysicalEOF));
+ }
+
+ //
+ // If this is a ELF_SKIP_DWORD, skip to the top of the file
+ //
+
+ if (*(PDWORD) NewPosition == ELF_SKIP_DWORD) {
+ NewPosition = PhysStart;
+ }
+
+ } else { // Reading backwards.
+
+ ASSERT (ReadFlags & EVENTLOG_BACKWARDS_READ);
+
+ if (CurrPosition == BeginRecord) {
+
+ //
+ // This is the "end of file" if we're reading backwards.
+ //
+
+ return(EndRecord);
+ }
+ else if (CurrPosition == PhysStart) {
+
+ //
+ // Flip to the bottom of the file, but skip and ELF_SKIP_DWORDs
+ //
+
+ FillDword = (PDWORD) PhysicalEOF; // last dword
+ FillDword--;
+ while (*FillDword == ELF_SKIP_DWORD) {
+ FillDword--;
+ }
+ CurrPosition = (PVOID) (FillDword + 1);
+ }
+
+ Length = *((PULONG) CurrPosition - 1);
+ if (Length < ELFEOFRECORDSIZE) { // Bogus length, must be invalid record
+ return(NULL);
+ }
+
+ NewPosition = (PVOID)((PBYTE) CurrPosition - Length);
+
+ //
+ // Take care of wrapping
+ //
+
+ if (NewPosition < PhysStart) {
+ NewPosition = (PVOID)((PBYTE) PhysicalEOF -
+ ((PBYTE) PhysStart - (PBYTE) NewPosition));
+ }
+ }
+ return (NewPosition);
+} // NextRecordPosition
+
+
+
+NTSTATUS
+SeekToStartingRecord (
+ PELF_REQUEST_RECORD Request,
+ PVOID *ReadPosition,
+ PVOID BeginRecord,
+ PVOID EndRecord,
+ PVOID PhysEOF,
+ PVOID PhysStart
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine seeks to the correct position as indicated in the
+ request packet.
+
+Arguments:
+
+ Pointer to the request packet.
+ Pointer to a pointer where the final position after the seek is returned.
+
+Return Value:
+
+ NTSTATUS and new position in file.
+
+Note:
+
+ This routine ensures that it is possible to seek to the position
+ specified in the request packet. If not, then an error is returned
+ which indicates that the file probably changed between the two
+ READ operations, or else the record offset specified is beyond the
+ end of the file.
+
+--*/
+{
+ PVOID Position;
+ ULONG RecordLen;
+ ULONG NumRecordsToSeek;
+ ULONG BytesPerRecord;
+ ULONG NumberOfRecords;
+ ULONG NumberOfBytes;
+ ULONG ReadFlags;
+
+ //
+ // If the beginning and the end are the same, then there are no
+ // entries in this file.
+ //
+ if (BeginRecord == EndRecord)
+ return (STATUS_END_OF_FILE);
+
+ //
+ // Find the last position (or the "beginning" if this is the first READ
+ // call for this handle).
+ //
+
+ if (Request->Pkt.ReadPkt->ReadFlags & EVENTLOG_SEQUENTIAL_READ) {
+
+ if (Request->Pkt.ReadPkt->ReadFlags & EVENTLOG_FORWARDS_READ) {
+
+ // If this is the first READ operation, LastSeekPosition will
+ // be zero. In that case, we set the position to the first
+ // record (in terms of time) in the file.
+ //
+ if (Request->Pkt.ReadPkt->LastSeekPos == 0) {
+
+ Position = BeginRecord;
+
+ } else {
+
+ Position = (PVOID)((PBYTE) Request->LogFile->BaseAddress
+ + Request->Pkt.ReadPkt->LastSeekPos );
+
+
+ //
+ // If we're changing the direction we're reading, skip
+ // forward one record. This is because we're pointing at
+ // the "next" record based on the last read direction
+ //
+
+ if (!(Request->Pkt.ReadPkt->Flags & ELF_LAST_READ_FORWARD)) {
+
+ Position = NextRecordPosition (
+ Request->Pkt.ReadPkt->ReadFlags,
+ Position,
+ ((PEVENTLOGRECORD) Position)->Length,
+ BeginRecord,
+ EndRecord,
+ PhysEOF,
+ PhysStart
+ );
+ }
+ else {
+ //
+ // This *really* cheesy check exists to handle the case
+ // where Position could be on an ELF_SKIP_DWORD pad
+ // dword at end of the file.
+ //
+ // NB: Must be prepared to handle an exception since
+ // a somewhat unknown pointer is dereferenced.
+ //
+ // BUGBUG: The eventlog code needs to handle exceptions
+ // in *many* more cases!
+ //
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ try {
+ if (IsPositionWithinRange(Position,
+ BeginRecord,
+ EndRecord))
+ {
+ //
+ // If this is a ELF_SKIP_DWORD, skip to the
+ // top of the file.
+ //
+
+ if (*(PDWORD) Position == ELF_SKIP_DWORD) {
+ Position = PhysStart;
+ }
+ } else {
+ //
+ // More likely the caller's handle was invalid
+ // if the position was not within range.
+ //
+
+ Status = STATUS_INVALID_HANDLE;
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = STATUS_EVENTLOG_FILE_CORRUPT;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ *ReadPosition = NULL;
+ return(Status);
+ }
+ }
+ }
+
+ } else { // READ backwards
+
+ // If this is the first READ operation, LastSeekPosition will
+ // be zero. In that case, we set the position to the last
+ // record (in terms of time) in the file.
+ //
+ if (Request->Pkt.ReadPkt->LastSeekPos == 0) {
+
+ Position = EndRecord;
+
+ //
+ // Subtract the length of the last record from the current
+ // position to get to the beginning of the record.
+ //
+ // If that moves beyond the physical beginning of the file,
+ // then we need to wrap around to the physical end of the file.
+ //
+
+ Position = (PVOID)((PBYTE)Position - *((PULONG)Position - 1));
+
+ if (Position < PhysStart) {
+ Position = (PVOID)((PBYTE)PhysEOF
+ - ((PBYTE)PhysStart - (PBYTE)Position));
+ }
+ } else {
+
+ Position = (PVOID)((PBYTE) Request->LogFile->BaseAddress
+ + Request->Pkt.ReadPkt->LastSeekPos );
+
+ //
+ // If we're changing the direction we're reading, skip
+ // forward one record. This is because we're pointing at
+ // the "next" record based on the last read direction
+ //
+
+ if (Request->Pkt.ReadPkt->Flags & ELF_LAST_READ_FORWARD) {
+
+ Position = NextRecordPosition (
+ Request->Pkt.ReadPkt->ReadFlags,
+ Position,
+ 0, // not used if reading backwards
+ BeginRecord,
+ EndRecord,
+ PhysEOF,
+ PhysStart
+ );
+ }
+ }
+ }
+
+ } else if (Request->Pkt.ReadPkt->ReadFlags & EVENTLOG_SEEK_READ) {
+
+ //
+ // Make sure the record number passed in is valid
+ //
+
+ if (Request->Pkt.ReadPkt->RecordNumber <
+ Request->LogFile->OldestRecordNumber ||
+ Request->Pkt.ReadPkt->RecordNumber >=
+ Request->LogFile->CurrentRecordNumber) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // We're seeking to an absolute record number, so use the following
+ // algorhythm:
+ //
+ // There are two defines that control the process:
+ //
+ // MAX_WALKING_DISTANCE - when we get this close to the record,
+ // we just sequentially read records till we find the right one
+ //
+ // MAX_TRIES - we'll only try this many times to get within
+ // walking distance by calculation using average record size,
+ // after this, we'll just brute force it from where we are
+ //
+ // Calculate the average number of bytes per record
+ //
+ // Based on this number seek to where the record should start
+ //
+ // Find the start of the next record in the file
+ //
+ // If it's within "walking distance" move forward sequentially
+ // to the right record
+ //
+ // If it's not, recalcuate average bytes per record for the records
+ // between the start and the current record, and repeat
+ //
+ // Have a max number of tries at this, then just walk from wherever
+ // we are to the right record
+ //
+
+#define MAX_WALKING_DISTANCE 5
+#define MAX_TRIES 5
+
+ //
+ // Calculate the average number of bytes per record
+ //
+
+ NumberOfRecords = Request->LogFile->CurrentRecordNumber -
+ Request->LogFile->OldestRecordNumber;
+ NumberOfBytes = Request->LogFile->Flags & ELF_LOGFILE_HEADER_WRAP ?
+ Request->LogFile->ActualMaxFileSize :
+ Request->LogFile->EndRecord;
+ NumberOfBytes -= FILEHEADERBUFSIZE;
+ BytesPerRecord = NumberOfBytes / NumberOfRecords;
+
+ //
+ // Calcuate the first guess as to what the offset of the desired
+ // record should be
+ //
+
+ Position = (PVOID) ((PBYTE) Request->LogFile->BaseAddress
+ + Request->LogFile->BeginRecord
+ + BytesPerRecord *
+ (Request->Pkt.ReadPkt->RecordNumber -
+ Request->LogFile->OldestRecordNumber));
+
+ //
+ // Align the position to a ULONG bountry.
+ //
+
+ Position = (PVOID) (((ULONG) Position + sizeof(ULONG) - 1) &
+ ~(sizeof(ULONG) - 1));
+
+ //
+ // Take care of file wrap
+ //
+
+ if (Position >= PhysEOF) {
+
+ Position = (PVOID)((PBYTE)PhysStart +
+ ((PBYTE) Position - (PBYTE) PhysEOF));
+ }
+ //
+ // Get to the start of the next record after position
+ //
+
+ Position = FindStartOfNextRecord(Position, BeginRecord, EndRecord,
+ PhysStart, PhysEOF, Request->LogFile->BaseAddress);
+
+ ASSERT(Position);
+
+ if (Request->Pkt.ReadPkt->RecordNumber >
+ ((PEVENTLOGRECORD) Position)->RecordNumber) {
+
+ NumRecordsToSeek = Request->Pkt.ReadPkt->RecordNumber -
+ ((PEVENTLOGRECORD) Position)->RecordNumber;
+ ReadFlags = EVENTLOG_FORWARDS_READ;
+ }
+
+ else {
+
+ NumRecordsToSeek = ((PEVENTLOGRECORD) Position)->RecordNumber -
+ Request->Pkt.ReadPkt->RecordNumber;
+ ReadFlags = EVENTLOG_BACKWARDS_READ;
+ }
+
+ ElfDbgPrint(("[ELF] Walking %d records\n", NumRecordsToSeek));
+
+ while (NumRecordsToSeek--) {
+
+ RecordLen = ((PEVENTLOGRECORD) Position)->Length;
+
+ Position = NextRecordPosition (
+ ReadFlags,
+ Position,
+ RecordLen,
+ BeginRecord,
+ EndRecord,
+ PhysEOF,
+ PhysStart
+ );
+ }
+
+ } // if SEEK_READ
+
+ *ReadPosition = Position; // This is the new seek position
+
+ if (!Position) { // The record was invalid
+ return(STATUS_EVENTLOG_FILE_CORRUPT);
+ }
+ else {
+ return (STATUS_SUCCESS);
+ }
+
+} // SeekToStartingRecord
+
+
+NTSTATUS
+ReadFromLog ( PELF_REQUEST_RECORD Request )
+
+/*++
+
+Routine Description:
+
+ This routine reads from the event log specified in the request packet.
+
+ This routine uses memory mapped I/O to access the log file. This makes
+ it much easier to move around the file.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NTSTATUS.
+
+Note:
+
+ When we come here, we are impersonating the client. If we get a
+ fault accessing the user's buffer, the fault goes to the user,
+ not the service.
+
+--*/
+{
+ NTSTATUS Status;
+ PVOID ReadPosition; // Current read position in file
+ PVOID XferPosition; // Position from where to copy bytes
+ PVOID BufferPosition; // Current position in user's buffer
+ ULONG BytesToMove; // Number of bytes to move
+ ULONG TotalBytesRead; // Total Bytes transferred
+ ULONG TotalRecordsRead; // Total records transferred
+ ULONG BytesInBuffer; // Bytes remaining in buffer
+ ULONG RecordSize; // Size of event record
+ PVOID PhysicalEOF; // Physical end of file
+ PVOID PhysStart; // Physical start of file (after file hdr)
+ PVOID BeginRecord; // Points to first record
+ PVOID EndRecord; // Points to byte after last record
+ PVOID TempBuf, TempBufferPosition;
+ ULONG RecordBytesTransferred;
+
+ //
+ // Initialize variables.
+ //
+
+ BytesInBuffer = Request->Pkt.ReadPkt->BufferSize;
+ BufferPosition = Request->Pkt.ReadPkt->Buffer;
+ TotalBytesRead = 0;
+ TotalRecordsRead = 0;
+ PhysicalEOF = (PVOID) ((LPBYTE) Request->LogFile->BaseAddress
+ + Request->LogFile->ViewSize);
+
+ PhysStart = (PVOID) ((LPBYTE)Request->LogFile->BaseAddress
+ + FILEHEADERBUFSIZE);
+
+ BeginRecord = (PVOID) ((LPBYTE) Request->LogFile->BaseAddress
+ + Request->LogFile->BeginRecord );// Start at first record
+
+ EndRecord = (PVOID) ((LPBYTE)Request->LogFile->BaseAddress
+ + Request->LogFile->EndRecord);// Byte after end of last record
+
+ //
+ // "Seek" to the starting record depending on either the last seek
+ // position, or the starting record offset passed in.
+ //
+
+ Status = SeekToStartingRecord (
+ Request,
+ &ReadPosition,
+ BeginRecord,
+ EndRecord,
+ PhysicalEOF,
+ PhysStart
+ );
+
+ if (NT_SUCCESS (Status) ) {
+
+ //
+ // Make sure the record is valid
+ //
+
+ if (!ValidFilePos(ReadPosition,
+ BeginRecord,
+ EndRecord,
+ PhysicalEOF,
+ Request->LogFile->BaseAddress
+ ))
+ {
+
+ Request->Pkt.ReadPkt->BytesRead = 0;
+ Request->Pkt.ReadPkt->RecordsRead = 0;
+
+ return (STATUS_INVALID_HANDLE);
+
+ }
+
+ RecordSize = *((PULONG)ReadPosition);
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ // While there are records to be read, and more space in the buffer,
+ // keep on reading records into the buffer.
+ //
+
+ while ( (RecordSize <= BytesInBuffer)
+ && (RecordSize != ELFEOFRECORDSIZE)) {
+
+ //
+ // If we were called by an ANSI API, then we need to read the
+ // next record into a temporary buffer, process the data in
+ // that record and copy it over to the real buffer as ANSI
+ // strings (rather than UNICODE).
+ //
+
+ if (Request->Pkt.ReadPkt->Flags & ELF_IREAD_ANSI) {
+
+ TempBuf = ElfpAllocateBuffer (RecordSize);
+
+ if (TempBuf == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ TempBufferPosition = BufferPosition; // Save this away
+ BufferPosition = TempBuf; // Read into TempBuf
+ }
+
+ //
+ // If the number of bytes to the end of the file is less than the
+ // size of the record, then part of the record has wrapped to the
+ // beginning of the file - transfer the bytes piece-meal.
+ //
+ // Otherwise, transfer the whole record.
+ //
+
+ BytesToMove = min (RecordSize, (ULONG)
+ ((PBYTE)(PhysicalEOF) -
+ (PBYTE)(ReadPosition)));
+
+ XferPosition = ReadPosition;
+
+ if ( BytesToMove < RecordSize) {
+
+ //
+ // We need to copy the bytes up to the end of the file,
+ // and then wrap around and copy the remaining bytes of
+ // this record.
+ //
+
+ ASSERT (BytesToMove + TotalBytesRead <=
+ Request->Pkt.ReadPkt->BufferSize);
+ RtlMoveMemory ( BufferPosition,
+ XferPosition,
+ BytesToMove
+ );
+
+ //
+ // Advance user buffer pointer.
+ // Move read position to the beginning of the file, past the
+ // file header.
+ // Update bytes remaining to be moved for this record.
+ //
+
+ BufferPosition = (PVOID) ((PBYTE)BufferPosition
+ + BytesToMove);
+
+ XferPosition = PhysStart;
+
+ BytesToMove = RecordSize - BytesToMove; // Remaining bytes
+
+ }
+
+ //
+ // Move the remaining bytes of the record OR the full record.
+ //
+
+ ASSERT (RecordSize + TotalBytesRead <=
+ Request->Pkt.ReadPkt->BufferSize);
+ RtlMoveMemory ( BufferPosition,
+ XferPosition,
+ BytesToMove
+ );
+
+ //
+ // Update to new read positions
+ //
+
+ BufferPosition = (PVOID) ((PBYTE)BufferPosition
+ + BytesToMove);
+
+ //
+ // If we were called by an ANSI API, then we need to take the
+ // record read into TempBuf and transfer it over to the user's
+ // buffer while converting any UNICODE strings to ANSI.
+ //
+
+ if (Request->Pkt.ReadPkt->Flags & ELF_IREAD_ANSI) {
+
+ Status = CopyUnicodeToAnsiRecord (
+ TempBufferPosition,
+ TempBuf,
+ &BufferPosition,
+ &RecordBytesTransferred
+ );
+
+ // RecordBytesTransferred contains the bytes actually
+ // copied into the user's buffer.
+ // BufferPosition points to the point in the user's buffer
+ // just after this record.
+ //
+ ElfpFreeBuffer (TempBuf); // Free the temp buffer
+
+ if (!NT_SUCCESS(Status)) {
+ break; // Exit this loop
+ }
+
+ } else {
+
+ RecordBytesTransferred = RecordSize;
+ }
+
+ //
+ // Update the byte and record counts
+ //
+
+ TotalRecordsRead++;
+ TotalBytesRead += RecordBytesTransferred;
+ BytesInBuffer -= RecordBytesTransferred;
+
+ ReadPosition = NextRecordPosition (
+ Request->Pkt.ReadPkt->ReadFlags,
+ ReadPosition,
+ RecordSize,
+ BeginRecord,
+ EndRecord,
+ PhysicalEOF,
+ PhysStart
+ );
+
+ if (!ReadPosition) {
+ return(STATUS_EVENTLOG_FILE_CORRUPT);
+ }
+
+ RecordSize = *(PULONG)ReadPosition;
+
+ } // while
+
+ //
+ // If we got to the end and did not read in any records, return
+ // an error indicating that the user's buffer is too small if
+ // we're not at the EOF record, or end of file if we are.
+ //
+
+ if (TotalRecordsRead == 0) {
+ if (RecordSize == ELFEOFRECORDSIZE) {
+ Status = STATUS_END_OF_FILE;
+ }
+
+ else {
+
+ //
+ // We didn't read any records, and we're not at EOF, so
+ // the buffer was too small
+ //
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+ Request->Pkt.ReadPkt->MinimumBytesNeeded = RecordSize;
+ }
+ }
+
+ //
+ // Update the current file position.
+ //
+
+ Request->Pkt.ReadPkt->LastSeekPos =
+ (ULONG)ReadPosition
+ - (ULONG)Request->LogFile->BaseAddress;
+ Request->Pkt.ReadPkt->LastSeekRecord += TotalRecordsRead;
+
+ }
+
+ //
+ // Set the bytes read in the request packet for return to client.
+ //
+
+ Request->Pkt.ReadPkt->BytesRead = TotalBytesRead;
+ Request->Pkt.ReadPkt->RecordsRead = TotalRecordsRead;
+
+ return (Status);
+
+} // ReadFromLog
+
+
+
+
+VOID
+PerformReadRequest ( PELF_REQUEST_RECORD Request )
+
+/*++
+
+Routine Description:
+
+ This routine performs the READ request.
+ It first grabs the log file structure resource and then proceeds
+ to read from the file. If the resource is not available, it will
+ block until it is.
+
+ This routine impersonates the client in order to ensure that the correct
+ access control is uesd. If the client does not have permission to read
+ the file, the operation will fail.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ //
+ // Get shared access to the log file. This will allow multiple
+ // readers to get to the file together.
+ //
+
+ RtlAcquireResourceShared (
+ &Request->Module->LogFile->Resource,
+ TRUE // Wait until available
+ );
+
+
+ //
+ // Set status field in the request packet.
+ //
+
+ Request->Status = (NTSTATUS) ReadFromLog ( Request );
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &Request->Module->LogFile->Resource );
+
+
+} // PerformReadRequest
+
+
+
+WCHAR wszAltDosDevices[] = L"\\DosDevices\\";
+WCHAR wszDosDevices[] = L"\\??\\";
+#define DOSDEVICES_LEN ((sizeof(wszDosDevices) / sizeof(WCHAR)) - 1)
+#define ALTDOSDEVICES_LEN ((sizeof(wszAltDosDevices) / sizeof(WCHAR)) - 1)
+
+
+VOID
+WriteToLog (
+ PLOGFILE pLogFile,
+ PVOID Buffer,
+ ULONG BufSize,
+ PULONG Destination,
+ ULONG PhysEOF,
+ ULONG PhysStart
+ )
+
+/*++
+
+Routine Description:
+
+ This routine writes the record into the log file, allowing for wrapping
+ around the end of the file.
+
+ It assumes that the caller has serialized access to the file, and has
+ ensured that there is enough space in the file for the record.
+
+Arguments:
+
+ Buffer - Pointer to the buffer containing the event record.
+ BufSize - Size of the record to be written.
+ Destination - Pointer to the destination - which is in the log file.
+ PhysEOF - Physical end of file.
+ PhysStart - Physical beginning of file (past the file header).
+
+Return Value:
+
+ NONE.
+
+Note:
+
+
+--*/
+{
+ ULONG BytesToCopy;
+ ULONG NewDestination;
+ NTSTATUS Status;
+ LARGE_INTEGER ByteOffset;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PVOID BaseAddress;
+ LPWSTR pwszLogFileName;
+
+ BytesToCopy = min (PhysEOF - *Destination, BufSize);
+
+ ByteOffset = RtlConvertUlongToLargeInteger (*Destination) ;
+ Status = NtWriteFile(
+ pLogFile->FileHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ Buffer, // Buffer
+ BytesToCopy, // Length
+ &ByteOffset, // Byteoffset
+ NULL); // Key
+
+ NewDestination = *Destination + BytesToCopy;
+
+ if (BytesToCopy != BufSize) {
+
+ //
+ // Wrap around to the beginning of the file and copy the
+ // rest of the data.
+ //
+
+ Buffer = (PVOID)((PBYTE) Buffer + BytesToCopy);
+
+ BytesToCopy = BufSize - BytesToCopy;
+
+ ByteOffset = RtlConvertUlongToLargeInteger (PhysStart);
+ Status = NtWriteFile(
+ pLogFile->FileHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ Buffer, // Buffer
+ BytesToCopy, // Length
+ &ByteOffset, // Byteoffset
+ NULL); // Key
+
+ NewDestination = PhysStart + BytesToCopy;
+
+ //
+ // Set "wrap" bit in log file structure
+ //
+
+ pLogFile->Flags |= ELF_LOGFILE_HEADER_WRAP;
+
+ //
+ // Now flush this to disk to commit it
+ //
+
+ BaseAddress = pLogFile->BaseAddress;
+ BytesToCopy = FILEHEADERBUFSIZE;
+
+ Status = NtFlushVirtualMemory(
+ NtCurrentProcess(),
+ &BaseAddress,
+ &BytesToCopy,
+ &IoStatusBlock
+ );
+ }
+
+ *Destination = NewDestination; // Return new destination
+
+ //
+ // Providing all succeeded above, if not set, set the archive file
+ // attribute on this log.
+ //
+
+ if (NT_SUCCESS(Status) && !(pLogFile->Flags & ELF_LOGFILE_ARCHIVE_SET)) {
+
+ //
+ // Advance past prefix string, '\??\' or '\DosDevices\'
+ //
+
+ if ((pLogFile->LogFileName->Length / 2) >= DOSDEVICES_LEN &&
+ !_wcsnicmp(wszDosDevices, pLogFile->LogFileName->Buffer,
+ DOSDEVICES_LEN)) {
+ pwszLogFileName = pLogFile->LogFileName->Buffer + DOSDEVICES_LEN;
+ }
+ else
+ if ((pLogFile->LogFileName->Length / 2) >= ALTDOSDEVICES_LEN &&
+ !_wcsnicmp(wszAltDosDevices, pLogFile->LogFileName->Buffer,
+ ALTDOSDEVICES_LEN)) {
+ pwszLogFileName = pLogFile->LogFileName->Buffer + ALTDOSDEVICES_LEN;
+ }
+ else {
+ pwszLogFileName = pLogFile->LogFileName->Buffer;
+ }
+
+ if (SetFileAttributes(pwszLogFileName, FILE_ATTRIBUTE_ARCHIVE)) {
+ pLogFile->Flags |= ELF_LOGFILE_ARCHIVE_SET;
+ }
+ else {
+ ElfDbgPrintNC(("[ELF] SetFileAttributes on file (%ws) failed, "
+ "WIN32 error = 0x%lx\n",
+ pwszLogFileName,
+ GetLastError()));
+ }
+ }
+
+} // WriteToLog
+
+
+
+VOID
+PerformWriteRequest ( PELF_REQUEST_RECORD Request)
+
+/*++
+
+Routine Description:
+
+ This routine writes the event log entry to the log file specified in
+ the request packet.
+ There is no need to impersonate the client since we want all clients
+ to have access to writing to the log file.
+
+ This routine does not use memory mapped I/O to access the log file. This
+ is so the changes can be immediately committed to disk if that was how
+ the log file was opened.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NONE
+
+Note:
+
+--*/
+{
+ static ULONG LastAlertWritten = 0; // Don't Generate alerts too often
+ NTSTATUS Status;
+ ULONG WritePos; // Position to write record
+ LARGE_INTEGER Time;
+ ULONG SpaceNeeded; // Record size + "buffer" size
+ ULONG CurrentTime;
+ PEVENTLOGRECORD EventRecord;
+ ULONG RecordSize;
+ ULONG DeletedRecordOffset;
+ ULONG SpaceAvail;
+ ULONG EarliestTime;
+ PLOGFILE pLogFile; // For optimized access to structure
+ PELF_LOGFILE_HEADER pFileHeader;
+ PVOID BaseAddress;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PEVENTLOGRECORD pEventLogRecord;
+ PDWORD FillDword;
+ ULONG OverwrittenEOF = 0;
+#ifdef _CAIRO_
+ BOOL fRaiseAlert = FALSE;
+#endif // _CAIRO_
+
+ pLogFile = Request->LogFile; // Set local variable
+
+ //
+ // Get exclusive access to the log file. This will ensure no one
+ // else is accessing the file.
+ //
+
+ RtlAcquireResourceExclusive (
+ &pLogFile->Resource,
+ TRUE // Wait until available
+ );
+
+ //
+ // Put in the record number
+ //
+
+ pEventLogRecord = (PEVENTLOGRECORD) Request->Pkt.WritePkt->Buffer;
+ pEventLogRecord->RecordNumber = pLogFile->CurrentRecordNumber;
+
+ //
+ // Now, go to the end of the file and look for empty space.
+ //
+ // If there is enough space to write out the record, just
+ // write it out and update the pointers.
+ //
+ // If there isn't enough space, then we need to check if we can
+ // wrap around the file without overwriting any records that are
+ // within the time retention period.
+ // If we cannot find any room, then we have to return an error
+ // that the file is full (and alert the administrator).
+ //
+
+ RecordSize = Request->Pkt.WritePkt->Datasize;
+
+ SpaceNeeded = RecordSize + ELFEOFRECORDSIZE;
+
+ if (pLogFile->EndRecord > pLogFile->BeginRecord) {
+
+ //
+ // The current write position is after the position of the first
+ // record, then we can write up to the end of the file without
+ // worrying about overwriting existing records.
+ //
+
+ SpaceAvail = pLogFile->ActualMaxFileSize - (pLogFile->EndRecord -
+ pLogFile->BeginRecord + FILEHEADERBUFSIZE);
+
+
+ } else if (pLogFile->EndRecord == pLogFile->BeginRecord
+ && !(pLogFile->Flags & ELF_LOGFILE_HEADER_WRAP)) {
+
+ //
+ // If the write position is equal to the position of the first
+ // record, and we have't wrapped yet, then the file is "empty"
+ // and so we have room to the physical end of the file.
+ //
+
+ SpaceAvail = pLogFile->ActualMaxFileSize - FILEHEADERBUFSIZE;
+
+ } else {
+
+ //
+ // If our write position is before the position of the first record, then
+ // the file has wrapped and we need to deal with overwriting existing
+ // records in the file.
+ //
+
+ SpaceAvail = pLogFile->BeginRecord - pLogFile->EndRecord;
+
+ }
+
+ //
+ // We now have the number of bytes available to write the record
+ // WITHOUT overwriting any existing records - in SpaceAvail.
+ // If that amount is not sufficient, then we need to create more space
+ // by "deleting" existing records that are older than the retention
+ // time that was configured for this file.
+ //
+ // We check the retention time against the time when the log was
+ // written since that is consistent at the server. We cannot use the
+ // client's time since that may vary if the clients are in different
+ // time zones.
+ //
+
+ NtQuerySystemTime(&Time);
+ RtlTimeToSecondsSince1970(&Time, &CurrentTime);
+
+ EarliestTime = CurrentTime - pLogFile->Retention;
+
+ Status = STATUS_SUCCESS; // Initialize for return to caller
+
+ //
+ // Check to see if the file hasn't reached it's maximum allowable
+ // size yet, and also hasn't wrapped. If not, grow it by as much as
+ // needed, in 64K chunks.
+ //
+
+ if (pLogFile->ActualMaxFileSize < pLogFile->ConfigMaxFileSize &&
+ SpaceNeeded > SpaceAvail &&
+ !(pLogFile->Flags & ELF_LOGFILE_HEADER_WRAP))
+ {
+
+ //
+ // Extend it. This call cannot fail. If it can't extend it, it
+ // just caps it at the current size by changing
+ // pLogFile->ConfigMaxFileSize
+ //
+
+ ElfExtendFile(pLogFile, SpaceNeeded, &SpaceAvail);
+ }
+
+ //
+ // We don't want to split the fixed portion of a record across the
+ // physical end of the file, it makes it difficult when referencing
+ // these fields later (you have to check before you touch each one
+ // to make sure it's not after the physical EOF). So, if there's
+ // not enough room at the end of the file for the fixed portion,
+ // we fill it with a known byte pattern ELF_SKIP_DWORD that will
+ // be skipped if it's found at the start of a record (as long as
+ // it's less than the minimum record size, then we know it's not
+ // the start of a valid record).
+ //
+
+ if (pLogFile->ActualMaxFileSize - pLogFile->EndRecord <
+ sizeof(EVENTLOGRECORD)) {
+
+ //
+ // Save the EndRecord pointer in case we don't have the space
+ // to write another record, we'll need to rewrite the EOF where
+ // it was
+ //
+
+ OverwrittenEOF = pLogFile->EndRecord;
+
+ FillDword = (PDWORD)((PBYTE) pLogFile->BaseAddress +
+ pLogFile->EndRecord);
+ while (FillDword < (PDWORD)((LPBYTE) pLogFile->BaseAddress +
+ pLogFile->ActualMaxFileSize)) {
+ *FillDword = ELF_SKIP_DWORD;
+ FillDword++;
+ }
+
+ pLogFile->EndRecord = FILEHEADERBUFSIZE;
+ SpaceAvail = pLogFile->BeginRecord - FILEHEADERBUFSIZE;
+ pLogFile->Flags |= ELF_LOGFILE_HEADER_WRAP;
+ }
+
+ EventRecord = (PEVENTLOGRECORD)((PBYTE) pLogFile->BaseAddress +
+ pLogFile->BeginRecord);
+
+ while ( SpaceNeeded > SpaceAvail ) {
+
+ //
+ // If this logfile can be overwrite-as-needed, or if it has
+ // an overwrite time limit and the time hasn't expired, then
+ // allow the new event to overwrite an older event.
+ //
+
+ if ((pLogFile->Retention == OVERWRITE_AS_NEEDED) ||
+ ((pLogFile->Retention != NEVER_OVERWRITE) &&
+ ((EventRecord->TimeWritten < EarliestTime) ||
+ (Request->Flags & ELF_FORCE_OVERWRITE)) )) { // OK to overwrite
+
+ DeletedRecordOffset = pLogFile->BeginRecord;
+
+ pLogFile->BeginRecord += EventRecord->Length;
+
+ if (pLogFile->BeginRecord >= pLogFile->ActualMaxFileSize) {
+
+ //
+ // We're about to wrap around the end of the file. If we
+ // have room to grow it do it now, but still move the
+ // pointer to the first record to be the next one in the file.
+ //
+
+ pLogFile->BeginRecord = FILEHEADERBUFSIZE +
+ ( pLogFile->BeginRecord -
+ pLogFile->ActualMaxFileSize);
+
+ //
+ // Check to see if the file has reached it's maximum allowable
+ // size yet. If not, grow it by as much as needed, in 64K
+ // chunks.
+ //
+
+ if (pLogFile->ActualMaxFileSize < pLogFile->ConfigMaxFileSize)
+ {
+
+ //
+ // Extend it. This call cannot fail. If it can't
+ // extend it, it just caps it at the current size by
+ // changing pLogFile->ConfigMaxFileSize
+ //
+
+ ElfExtendFile(pLogFile, SpaceNeeded, &SpaceAvail);
+
+ //
+ // Since extending the file will cause it to be moved, we
+ // need to re-establish the address for the EventRecord.
+ //
+ EventRecord = (PEVENTLOGRECORD)((PBYTE) pLogFile->BaseAddress +
+ DeletedRecordOffset);
+ }
+ }
+
+ //
+ // Make sure no handle points to the record that we're getting
+ // ready to overwrite, it one does, correct it to point to the
+ // new first record.
+ //
+
+ FixContextHandlesForRecord(DeletedRecordOffset,
+ pLogFile->BeginRecord);
+
+ SpaceAvail += EventRecord->Length;
+
+ //
+ // Bump to the next record, file wrap was handled above
+ //
+
+ //
+ // If these are ELF_SKIP_DWORDs, just move past them
+ //
+
+ FillDword = (PDWORD)((PBYTE) pLogFile->BaseAddress +
+ pLogFile->BeginRecord);
+
+ if (*FillDword == ELF_SKIP_DWORD) {
+ SpaceAvail += pLogFile->ActualMaxFileSize -
+ pLogFile->BeginRecord;
+ pLogFile->BeginRecord = FILEHEADERBUFSIZE;
+ }
+
+ EventRecord = (PEVENTLOGRECORD)((PBYTE) pLogFile->BaseAddress +
+ pLogFile->BeginRecord);
+
+ } else { // All records within retention period
+
+ ElfDbgPrint(("[ELF] Log file is full\n"));
+
+ //
+ // Hang an event on the queuedevent list for later writing
+ // if we haven't just written a log full event for this log
+ //
+
+ if (!(pLogFile->Flags & ELF_LOGFILE_LOGFULL_WRITTEN)) {
+
+ //
+ // Don't generate log full events/alerts more often than
+ // once every hour
+ //
+
+ if (!LastAlertWritten ||
+ CurrentTime > LastAlertWritten + 60 * 60) {
+
+ pLogFile->Flags |= ELF_LOGFILE_LOGFULL_WRITTEN;
+
+ //
+ // Per bug #4960, it is considered a C2 security breach
+ // if a "security log full" message is posted to the
+ // system log *and* the current user's console. An admin
+ // alert is OK.
+ //
+
+ if (_wcsicmp(pLogFile->LogModuleName->Buffer,
+ ELF_SECURITY_MODULE_NAME) != 0) {
+
+ ElfpCreateElfEvent(
+ EVENT_LOG_FULL,
+ EVENTLOG_ERROR_TYPE,
+ 0, // EventCategory
+ 1, // NumberOfStrings
+ &Request->LogFile->LogModuleName->
+ Buffer, // Strings
+ NULL, // Data
+ 0, // Datalength
+ ELF_FORCE_OVERWRITE); // Overwrite if necc.
+
+ ElfpCreateQueuedMessage(ALERT_ELF_LogOverflow, 1,
+ &Request->Module->LogFile->LogModuleName->Buffer);
+ }
+
+ LastAlertWritten = CurrentTime;
+ ElfpCreateQueuedAlert(ALERT_ELF_LogOverflow, 1,
+ &Request->Module->LogFile->LogModuleName->Buffer);
+ }
+ }
+ if (OverwrittenEOF) {
+
+ //
+ // The EOF record was at the end of the physical file,
+ // and we overwrote it with ELF_SKIP_DWORDs, so we need
+ // to put it back since we're not going to be able to
+ // write a record. We also need to turn the wrap bit
+ // back off
+ //
+
+ pLogFile->Flags &= ~(ELF_LOGFILE_HEADER_WRAP);
+ pLogFile->EndRecord = OverwrittenEOF;
+ WritePos = OverwrittenEOF;
+
+ //
+ // Write out the EOF record
+ //
+
+ WriteToLog ( pLogFile,
+ (PVOID) &EOFRecord,
+ ELFEOFRECORDSIZE,
+ &WritePos,
+ pLogFile->ActualMaxFileSize,
+ FILEHEADERBUFSIZE
+ );
+ }
+
+ Status = STATUS_LOG_FILE_FULL;
+ break; // Get out of while loop
+
+ }
+ }
+
+ if (NT_SUCCESS (Status)) {
+
+ //
+ // We have enough room to write the record and the EOF record.
+ //
+
+ //
+ // Update OldestRecordNumber to reflect the records that were
+ // overwritten amd increment the CurrentRecordNumber
+ //
+ // Make sure that the log isn't empty, if it is, the oldestrecord
+ // is 1
+ //
+
+ if (pLogFile->BeginRecord == pLogFile->EndRecord) {
+ pLogFile->OldestRecordNumber = 1;
+ }
+ else {
+ pLogFile->OldestRecordNumber = EventRecord->RecordNumber;
+ }
+ pLogFile->CurrentRecordNumber++;
+
+ //
+ // If the dirty bit is not set, then this is the first time that
+ // we have written to the file since we started. In that case,
+ // set the dirty bit in the file header as well so that we will
+ // know that the contents have changed.
+ //
+
+ if ( !(pLogFile->Flags & ELF_LOGFILE_HEADER_DIRTY) ) {
+ ULONG HeaderSize;
+
+ pLogFile->Flags |= ELF_LOGFILE_HEADER_DIRTY;
+
+ pFileHeader = (PELF_LOGFILE_HEADER)(pLogFile->BaseAddress);
+ pFileHeader->Flags |= ELF_LOGFILE_HEADER_DIRTY;
+
+ //
+ // Now flush this to disk to commit it
+ //
+
+ BaseAddress = pLogFile->BaseAddress;
+ HeaderSize = FILEHEADERBUFSIZE;
+
+ Status = NtFlushVirtualMemory(
+ NtCurrentProcess(),
+ &BaseAddress,
+ &HeaderSize,
+ &IoStatusBlock
+ );
+ }
+
+ //
+ // Now write out the record
+ //
+
+ WriteToLog ( pLogFile,
+ Request->Pkt.WritePkt->Buffer,
+ RecordSize,
+ &(pLogFile->EndRecord),
+ pLogFile->ActualMaxFileSize,
+ FILEHEADERBUFSIZE
+ );
+
+ //
+ // Use a separate variable for the position,since we don't want
+ // it updated.
+ //
+
+ WritePos = pLogFile->EndRecord;
+ if (WritePos > pLogFile->ActualMaxFileSize) {
+ WritePos -= pLogFile->ActualMaxFileSize -
+ FILEHEADERBUFSIZE;
+ }
+
+ //
+ // Update the EOF record fields
+ //
+
+ EOFRecord.BeginRecord = pLogFile->BeginRecord;
+ EOFRecord.EndRecord = WritePos;
+ EOFRecord.CurrentRecordNumber = pLogFile->CurrentRecordNumber;
+ EOFRecord.OldestRecordNumber = pLogFile->OldestRecordNumber;
+
+ //
+ // Write out the EOF record
+ //
+
+ WriteToLog ( pLogFile,
+ (PVOID) &EOFRecord,
+ ELFEOFRECORDSIZE,
+ &WritePos,
+ pLogFile->ActualMaxFileSize,
+ FILEHEADERBUFSIZE
+ );
+
+
+ //
+ // If we had just written a logfull record, turn the bit off.
+ // Since we just wrote a record, technically it's not full anymore
+ //
+
+ if (!(Request->Flags & ELF_FORCE_OVERWRITE)) {
+ pLogFile->Flags &= ~(ELF_LOGFILE_LOGFULL_WRITTEN);
+ }
+
+ //
+ // See if there are any ElfChangeNotify callers to notify, and if
+ // there are, pulse their event
+ //
+
+ NotifyChange(pLogFile);
+
+#ifdef _CAIRO_
+ //
+ // ** NEW FOR CAIRO **
+ //
+ // Test if this event should be raised as a Cairo alert. Notice this
+ // test is performed within logfile resource exclusion while actual
+ // raise of the event as a Cairo alert is performed outside of
+ // it - there is no need to serialize access to the logfile while
+ // raising the event as an alert.
+ //
+ // Raise *only* events written to the System log.
+ //
+
+ if (_wcsicmp(ELF_SYSTEM_MODULE_NAME,
+ Request->LogFile->LogModuleName->Buffer) == 0)
+ {
+ fRaiseAlert = TestFilter(pEventLogRecord->EventType,
+ Request->Module->AlertSeverity);
+ }
+#endif // _CAIRO_
+ }
+
+ //
+ // Set status field in the request packet.
+ //
+
+ Request->Status = (NTSTATUS) Status;
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &pLogFile->Resource );
+
+#ifdef _CAIRO_
+ //
+ // ** NEW FOR CAIRO **
+ //
+ // Raise the event as a Cairo alert to the local computer distributor
+ // object if the filter match test succeeded above.
+ //
+ // Note: this is done outside of log resource exclusion since raise has
+ // nothing to do with the log and its data structures.
+ //
+
+ if (fRaiseAlert)
+ {
+ //
+ // Don't care if this fails right now.
+ //
+
+ RaiseCairoAlert(Request->Module, pEventLogRecord);
+ }
+#endif // _CAIRO_
+
+} // PerformWriteRequest
+
+
+VOID
+PerformClearRequest( PELF_REQUEST_RECORD Request)
+
+/*++
+
+Routine Description:
+
+ This routine will optionally back up the log file specified, and will
+ delete it.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NONE
+
+Note:
+
+ On the exit path, when we do some "cleanup" work, we discard the
+ status and instead return the status of the operation that is being
+ performed.
+ This is necessary since we wish to return any error condition that is
+ directly related to the clear operation. For other errors, we will
+ fail at a later stage.
+
+--*/
+{
+ NTSTATUS Status, IStatus;
+ PUNICODE_STRING FileName;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PFILE_RENAME_INFORMATION NewName = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE ClearHandle;
+ FILE_DISPOSITION_INFORMATION DeleteInfo = {TRUE};
+ ULONG FileRefCount;
+ BOOLEAN FileRenamed = FALSE;
+
+ //
+ // Get exclusive access to the log file. This will ensure no one
+ // else is accessing the file.
+ //
+
+ RtlAcquireResourceExclusive (
+ &Request->Module->LogFile->Resource,
+ TRUE // Wait until available
+ );
+
+ //
+ // We have exclusive access to the file.
+ //
+ // We force the file to be closed, and store away the ref count
+ // so that we can set it back when we reopen the file.
+ // This is a little *sleazy* but we have exclusive access to the
+ // logfile structure so we can play these games.
+ //
+
+ FileRefCount = Request->LogFile->RefCount; // Store this away
+ ElfpCloseLogFile ( Request->LogFile, ELF_LOG_CLOSE_FORCE );
+ Request->LogFile->FileHandle = NULL; // For use later
+
+ //
+ // Open the file with delete access in order to rename it.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ Request->LogFile->LogFileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenFile(&ClearHandle,
+ GENERIC_READ | DELETE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_DELETE,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ // If the backup file name has been specified and is not NULL,
+ // then we move the current file to the new file. If that fails,
+ // then fail the whole operation.
+ //
+
+ if ( (Request->Pkt.ClearPkt->BackupFileName != NULL)
+ && (Request->Pkt.ClearPkt->BackupFileName->Length != 0)) {
+
+ FileName = Request->Pkt.ClearPkt->BackupFileName;
+
+ //
+ // Set up the rename information structure with the new name
+ //
+
+ NewName = ElfpAllocateBuffer(
+ FileName->Length + sizeof(WCHAR) + sizeof(*NewName));
+ if (NewName) {
+ RtlMoveMemory( NewName->FileName,
+ FileName->Buffer,
+ FileName->Length
+ );
+
+ //
+ // Guarantee that it's NULL terminated
+ //
+
+ NewName->FileName[FileName->Length / sizeof(WCHAR)] = L'\0';
+
+ NewName->ReplaceIfExists = FALSE;
+ NewName->RootDirectory = NULL;
+ NewName->FileNameLength = FileName->Length;
+
+ Status = NtSetInformationFile(
+ ClearHandle,
+ &IoStatusBlock,
+ NewName,
+ FileName->Length+sizeof(*NewName),
+ FileRenameInformation
+ );
+
+ if (Status == STATUS_NOT_SAME_DEVICE) {
+
+ //
+ // They want the backup file to be on a different
+ // device. We need to copy this one, and then delete
+ // it.
+ //
+
+ ElfDbgPrint(("[ELF] Copy log file\n"));
+
+ Status = ElfpCopyFile(ClearHandle, FileName);
+
+ if (NT_SUCCESS(Status)) {
+ ElfDbgPrint(("[ELF] Deleting log file\n"));
+
+ Status = NtSetInformationFile(
+ ClearHandle,
+ &IoStatusBlock,
+ &DeleteInfo,
+ sizeof(DeleteInfo),
+ FileDispositionInformation
+ );
+
+ if ( !NT_SUCCESS (Status) ) {
+ ElfDbgPrintNC(("[ELF] Delete failed 0x%lx\n",
+ Status));
+ }
+ }
+ }
+ else if ( NT_SUCCESS (Status) ) {
+ FileRenamed = TRUE;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrintNC(("[ELF] Rename/Copy failed 0x%lx\n", Status));
+ }
+ } else {
+ Status = STATUS_NO_MEMORY;
+ }
+ } else { // No backup to done
+
+ //
+ // No backup name was specified. Just delete the log file
+ // (i.e. "clear it"). We can just delete it since we know
+ // that the first time anything is written to a log file,
+ // if that file does not exist, it is created and a header
+ // is written to it. By deleting it here, we make it cleaner
+ // to manage log files, and avoid having zero-length files all
+ // over the disk.
+ //
+
+ ElfDbgPrint(("[ELF] Deleting log file\n"));
+
+ Status = NtSetInformationFile(
+ ClearHandle,
+ &IoStatusBlock,
+ &DeleteInfo,
+ sizeof(DeleteInfo),
+ FileDispositionInformation
+ );
+
+ if ( !NT_SUCCESS (Status) ) {
+ ElfDbgPrintNC(("[ELF] Delete failed 0x%lx\n", Status));
+
+ }
+
+
+ } // Backup and/or Delete
+
+ IStatus = NtClose (ClearHandle); // Discard status
+ ASSERT (NT_SUCCESS (IStatus));
+
+ } else { // The open-for-delete failed.
+
+ ElfDbgPrintNC(("[ELF] Open-for-delete failed 0x%lx\n", Status));
+ }
+
+ //
+ // Now pick up any new size value that was set before but
+ // couldn't be used until the log was cleared (they reduced the size of
+ // the log file.)
+ //
+
+ if (NT_SUCCESS (Status)) {
+ if (Request->LogFile->NextClearMaxFileSize) {
+ Request->LogFile->ConfigMaxFileSize =
+ Request->LogFile->NextClearMaxFileSize;
+ }
+ }
+
+ //
+ // We need to recreate the file or if the file was just closed,
+ // then we reopen it.
+ //
+
+ IStatus = ElfOpenLogFile ( Request->LogFile, ElfNormalLog);
+ if (!NT_SUCCESS(IStatus)) {
+
+ //
+ // Opening the new log file failed, reopen the old log and
+ // return this error from the Api
+ //
+
+ PFILE_RENAME_INFORMATION OldName;
+ UNICODE_STRING UnicodeString;
+
+ Status = IStatus;
+
+ //
+ // There shouldn't be any way to fail unless we successfully
+ // renamed the file, and there's no recovery if that happens.
+ //
+
+ ASSERT(FileRenamed == TRUE);
+
+ //
+ // Rename the file back to the original name. Reuse ClearHandle.
+ //
+
+ RtlInitUnicodeString(&UnicodeString, NewName->FileName);
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeString,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ IStatus = NtOpenFile(&ClearHandle,
+ GENERIC_READ | DELETE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_DELETE,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ //
+ // This can't fail, I just created it!
+ //
+
+ ASSERT(NT_SUCCESS(IStatus));
+
+ //
+ // Set up the rename information structure with the old name
+ //
+
+ OldName = ElfpAllocateBuffer(
+ Request->LogFile->LogFileName->Length + sizeof(WCHAR) +
+ sizeof(*OldName));
+
+ if (OldName) {
+ RtlMoveMemory( OldName->FileName,
+ Request->LogFile->LogFileName->Buffer,
+ Request->LogFile->LogFileName->Length
+ );
+
+
+ //
+ // Guarantee that it's NULL terminated
+ //
+
+ OldName->FileName[Request->LogFile->LogFileName->Length /
+ sizeof(WCHAR)] = L'\0';
+
+ OldName->ReplaceIfExists = FALSE;
+ OldName->RootDirectory = NULL;
+ OldName->FileNameLength = Request->LogFile->LogFileName->Length;
+
+ IStatus = NtSetInformationFile(
+ ClearHandle,
+ &IoStatusBlock,
+ OldName,
+ Request->LogFile->LogFileName->Length +
+ sizeof(*OldName) + sizeof(WCHAR),
+ FileRenameInformation
+ );
+ ASSERT(NT_SUCCESS(IStatus));
+ IStatus = NtClose(ClearHandle);
+ ASSERT(NT_SUCCESS(IStatus));
+
+ //
+ // Reopen the original file, this has to work
+ //
+
+ IStatus = ElfOpenLogFile ( Request->LogFile, ElfNormalLog);
+ ASSERT(NT_SUCCESS(IStatus));
+
+ ElfpFreeBuffer(OldName);
+ }
+ }
+
+ Request->LogFile->RefCount = FileRefCount; // Restore old value.
+
+ //
+ // Mark any open context handles that point to this file as "invalid for
+ // read". This will fail any further READ operations and force the caller
+ // to close and reopen the handle.
+ //
+
+ InvalidateContextHandlesForLogFile ( Request->LogFile );
+
+
+ //
+ // Set status field in the request packet.
+ //
+
+ Request->Status = Status;
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &Request->Module->LogFile->Resource );
+ if (NewName) {
+ ElfpFreeBuffer (NewName);
+ }
+
+
+} // PerformClearRequest
+
+
+VOID
+PerformBackupRequest( PELF_REQUEST_RECORD Request)
+
+/*++
+
+Routine Description:
+
+ This routine will back up the log file specified.
+
+ This routine impersonates the client in order to ensure that the correct
+ access control is used.
+
+ This routine is entered with the ElfGlobalResource held in a shared
+ state and the logfile lock is acquired shared to prevent writing, but
+ allow people to still read.
+
+ This copies the file in two chunks, from the first record to the end
+ of the file, and then from the top of the file (excluding the header)
+ to the end of the EOF record.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NONE, status is placed in the packet for later use by the API wrapper
+
+--*/
+{
+ NTSTATUS Status, IStatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LARGE_INTEGER MaximumSizeOfSection;
+ LARGE_INTEGER Offset;
+ ULONG LastRecordNumber;
+ ULONG OldestRecordNumber;
+ HANDLE BackupHandle;
+ PBYTE StartOfCopy;
+ PBYTE EndOfCopy;
+ ULONG BytesToCopy;
+ ULONG EndRecord = FILEHEADERBUFSIZE;
+ BOOL ImpersonatingClient = FALSE;
+ ELF_LOGFILE_HEADER FileHeaderBuf = { FILEHEADERBUFSIZE, // Size
+ ELF_LOG_FILE_SIGNATURE,
+ ELF_VERSION_MAJOR,
+ ELF_VERSION_MINOR,
+ FILEHEADERBUFSIZE, // Start offset
+ FILEHEADERBUFSIZE, // End offset
+ 1, // Next record #
+ 1, // Oldest record #
+ 0, // Maxsize
+ 0, // Flags
+ 0, // Retention
+ FILEHEADERBUFSIZE // Size
+ };
+
+
+ //
+ // Get shared access to the log file. This will ensure no one
+ // else clears the file.
+ //
+
+ RtlAcquireResourceShared (
+ &Request->Module->LogFile->Resource,
+ TRUE // Wait until available
+ );
+
+ //
+ // Save away the next record number. We'll stop copying when we get to
+ // the record before this one. Also save the first record number so we
+ // can update the header and EOF record.
+ //
+
+ LastRecordNumber = Request->LogFile->CurrentRecordNumber;
+ OldestRecordNumber = Request->LogFile->OldestRecordNumber;
+
+ //
+ // Impersonate the client
+ //
+
+ Status = ElfImpersonateClient ();
+
+ if (NT_SUCCESS (Status)) {
+
+ //
+ // Keep this info so I can only revert in 1 place
+ //
+
+ ImpersonatingClient = TRUE;
+
+ //
+ // Set up the object attributes structure for the backup file
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ Request->Pkt.BackupPkt->BackupFileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Open the backup file. Fail if a file by this name already exists.
+ //
+
+ MaximumSizeOfSection =
+ RtlConvertUlongToLargeInteger (
+ Request->LogFile->ActualMaxFileSize);
+
+ Status = NtCreateFile(
+ &BackupHandle,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ &MaximumSizeOfSection,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_CREATE,
+ FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ 0);
+
+ if (!NT_SUCCESS(Status)) {
+ ElfDbgPrintNC(("[ELF] Open of Backup file failed - %X\n", Status));
+ goto errorexit;
+ }
+
+ //
+ // Write out the header, we'll update it later
+ //
+
+ FileHeaderBuf.CurrentRecordNumber = LastRecordNumber;
+ FileHeaderBuf.OldestRecordNumber = OldestRecordNumber;
+ FileHeaderBuf.Flags = 0;
+ FileHeaderBuf.Retention = Request->LogFile->Retention;
+
+ Status = NtWriteFile(
+ BackupHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &FileHeaderBuf, // Buffer
+ FILEHEADERBUFSIZE, // Length
+ NULL, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF]: Backup file header write failed %X\n",
+ Status));
+ goto errorexit;
+ }
+
+ //
+ // Scan from the end of the file skipping over ELF_SKIP_DWORDs
+ // to figure out far to copy. If we haven't wrapped, we just
+ // copy to the EndRecord offset.
+ //
+
+ if (Request->LogFile->Flags & ELF_LOGFILE_HEADER_WRAP) {
+ EndOfCopy = (PBYTE) Request->LogFile->BaseAddress
+ + Request->LogFile->ActualMaxFileSize - sizeof(DWORD);
+ while (*((PDWORD)EndOfCopy) == ELF_SKIP_DWORD) {
+ EndOfCopy -= sizeof(DWORD);
+ }
+ EndOfCopy += sizeof(DWORD);
+
+ }
+ else {
+ EndOfCopy = (PBYTE) Request->LogFile->BaseAddress +
+ Request->LogFile->EndRecord;
+ }
+
+ //
+ // Now set the start position to be the first record and
+ // calculate the number of bytes to copy
+ //
+
+ StartOfCopy = (PBYTE) Request->LogFile->BaseAddress +
+ Request->LogFile->BeginRecord;
+
+ BytesToCopy = EndOfCopy - StartOfCopy;
+ EndRecord += BytesToCopy;
+
+ Status = NtWriteFile(
+ BackupHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ StartOfCopy, // Buffer
+ BytesToCopy, // Length
+ NULL, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF]: Backup file 1st block write failed %X\n",
+ Status));
+ goto errorexit;
+ }
+
+ //
+ // If the file's not wrapped, we're done except for the EOF
+ // record. If the file is wrapped we have to copy the 2nd
+ // piece
+ //
+
+ if (Request->LogFile->Flags & ELF_LOGFILE_HEADER_WRAP) {
+ StartOfCopy = (PBYTE) Request->LogFile->BaseAddress +
+ FILEHEADERBUFSIZE;
+ EndOfCopy = (PBYTE) Request->LogFile->BaseAddress +
+ Request->LogFile->EndRecord;
+
+ BytesToCopy = EndOfCopy - StartOfCopy;
+ EndRecord += BytesToCopy;
+
+ Status = NtWriteFile(
+ BackupHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ StartOfCopy, // Buffer
+ BytesToCopy, // Length
+ NULL, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF]: Backup file 2nd block write failed %X\n",
+ Status));
+ goto errorexit;
+ }
+ }
+
+ //
+ // Write out the EOF record after updating the fields needed for
+ // recovery.
+ //
+
+ EOFRecord.BeginRecord = FILEHEADERBUFSIZE;
+ EOFRecord.EndRecord = EndRecord;
+ EOFRecord.CurrentRecordNumber = LastRecordNumber;
+ EOFRecord.OldestRecordNumber = OldestRecordNumber;
+
+ Status = NtWriteFile(
+ BackupHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &EOFRecord, // Buffer
+ ELFEOFRECORDSIZE, // Length
+ NULL, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF]: Backup file EOF record write failed %X\n",
+ Status));
+ goto errorexit;
+ }
+
+ //
+ // Update the header with valid information
+ //
+
+ FileHeaderBuf.EndOffset = EndRecord;
+ FileHeaderBuf.MaxSize = EndRecord + ELFEOFRECORDSIZE;
+ Offset = RtlConvertUlongToLargeInteger (0);
+
+ Status = NtWriteFile(
+ BackupHandle, // Filehandle
+ NULL, // Event
+ NULL, // APC routine
+ NULL, // APC context
+ &IoStatusBlock, // IO_STATUS_BLOCK
+ &FileHeaderBuf, // Buffer
+ FILEHEADERBUFSIZE, // Length
+ &Offset, // Byteoffset
+ NULL); // Key
+
+
+ if (!NT_SUCCESS(Status)) {
+
+ ElfDbgPrintNC(("[ELF]: Backup file header rewrite failed %X\n",
+ Status));
+ goto errorexit;
+ }
+
+ //
+ // Clear the LogFile flag archive bit, assuming the caller will
+ // clear (or has cleared) this log's archive file attribute.
+ // Note: No big deal if the caller didn't clear the archive
+ // attribute.
+ //
+ // The next write to this log tests the LogFile flag archive bit.
+ // If the bit is clear, the archive file attribute is set on the
+ // log file.
+ //
+
+ Request->LogFile->Flags &= ~ELF_LOGFILE_ARCHIVE_SET;
+
+ //
+ // Close the output file
+ //
+ // BUGBUG : markbl (4/6/95) shouldn't close be done on error also?
+ //
+
+ NtClose(BackupHandle);
+
+ //
+ // Undo the impersonation.
+ //
+
+ }
+errorexit:
+
+ if (ImpersonatingClient) {
+ IStatus = ElfRevertToSelf (); // Discard status
+ ASSERT (NT_SUCCESS (IStatus));
+ }
+
+ //
+ // Set status field in the request packet.
+ //
+
+ Request->Status = Status;
+
+ //
+ // Free the resource
+ //
+
+ RtlReleaseResource ( &Request->Module->LogFile->Resource );
+
+} // PerformBackupRequest
+
+
+VOID
+ElfPerformRequest( PELF_REQUEST_RECORD Request)
+
+/*++
+
+Routine Description:
+
+ This routine takes the request packet and performs the operation
+ on the event log.
+ Before it does that, it takes the Global serialization resource
+ for a READ to prevent other threads from doing WRITE operations on
+ the resources of the service.
+
+ After it has performed the requested operation, it writes any records
+ generated by the eventlog service that have been put on the queuedevent
+ list.
+
+Arguments:
+
+ Pointer to the request packet.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+
+ BOOL Acquired = FALSE;
+
+ //
+ // Acquire the global resource for shared access. If the resource is
+ // not immediately available (i.e. don't wait), then some other thread
+ // has it out for exclusive access.
+ //
+ // In that case, we can do one of two things:
+ //
+ // 1) Thread monitoring the registry
+ // We can wait for this thread to finish so that the
+ // operation can continue.
+ //
+ // 2) Control thread
+ // In this case, it may turn out that the service will
+ // be terminated or paused. We can examine the current
+ // status of the service and see if it is still "installed"
+ // (i.e. no "pending" state). If so, we loop around and try
+ // to get the resource again (after sleeping a bit?). We
+ // break out of the loop if the state of the service changes
+ // to PAUSED, PAUSE_PENDING, UNINSTALL_PENDING, etc. so as
+ // not to block the thread indefinitely.
+ //
+
+ while ( (GetElState() == RUNNING) && (!Acquired)) {
+
+ Acquired = RtlAcquireResourceShared(
+ &GlobalElfResource,
+ FALSE // Don't wait
+ );
+ if (!Acquired) {
+ ElfDbgPrint(("[ELF] Sleep waiting for global resource\n" ));
+ Sleep (ELF_GLOBAL_RESOURCE_WAIT);
+ }
+
+ }
+
+ // If the resource was not available and the status of the service
+ // changed to one of the "non-working" states, then we just return
+ // unsuccesful. Rpc should not allow this to happen.
+ //
+
+ if (!Acquired) {
+
+ ElfDbgPrint(("[ELF] Global resource not acquired.\n" ));
+ Request->Status = STATUS_UNSUCCESSFUL;
+
+ } else {
+
+ switch ( Request->Command ) {
+
+ case ELF_COMMAND_READ:
+ PerformReadRequest( Request );
+ break;
+
+ case ELF_COMMAND_WRITE:
+ PerformWriteRequest ( Request );
+ break;
+
+ case ELF_COMMAND_CLEAR:
+ PerformClearRequest( Request );
+ break;
+
+ case ELF_COMMAND_BACKUP:
+ PerformBackupRequest( Request );
+ break;
+ case ELF_COMMAND_WRITE_QUEUED:
+ break;
+ }
+
+ //
+ // Now run the queued event list dequeueing elements and
+ // writing them
+ //
+
+ if (!IsListEmpty(&QueuedEventListHead)) {
+
+ //
+ // There are things queued up to write, do it
+ //
+
+ WriteQueuedEvents();
+ }
+
+ //
+ // Release the global resource.
+ //
+
+ ReleaseGlobalResource();
+
+ }
+
+} // ElfPerformRequest
+
+#if DBG
+VOID
+ElfErrorOut(
+ LPSTR ErrorText,
+ DWORD StatusCode,
+ PLOGFILE pLogFile)
+{
+ DbgPrint("\n[EVENTLOG]: %s,0x%lx, \n\t%ws Log:\n"
+ "\tConfigMaxSize = 0x%lx, ActualMax = 0x%lx, BaseAddr = 0x%lx\n"
+ "\tViewSize = 0x%lx, EndRec = 0x%lx\n",
+ ErrorText,
+ StatusCode,
+ pLogFile->LogModuleName->Buffer,
+ pLogFile->ConfigMaxFileSize,
+ pLogFile->ActualMaxFileSize,
+ pLogFile->BaseAddress,
+ pLogFile->ViewSize,
+ pLogFile->EndRecord);
+
+ //DebugBreak();
+}
+#endif //DBG
diff --git a/private/eventlog/server/terminat.c b/private/eventlog/server/terminat.c
new file mode 100644
index 000000000..7a2f0d519
--- /dev/null
+++ b/private/eventlog/server/terminat.c
@@ -0,0 +1,267 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ TERMINAT.C
+
+Abstract:
+
+ This file contains all the cleanup routines for the Eventlog service.
+ These routines are called when the service is terminating.
+
+Author:
+
+ Rajen Shah (rajens) 09-Aug-1991
+
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <eventp.h>
+#include <ntrpcp.h>
+
+
+
+
+VOID
+StopLPCThread ()
+
+/*++
+
+Routine Description:
+
+ This routine stops the LPC thread and cleans up LPC-related resources.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+--*/
+
+{
+ ElfDbgPrint(( "[ELF] Stop the LPC thread\n" ));
+
+ //
+ // Close communication port handle
+ //
+
+ NtClose ( ElfCommunicationPortHandle );
+
+ //
+ // Close connection port handle
+ //
+
+ NtClose ( ElfConnectionPortHandle );
+
+ //
+ // Terminate the LPC thread.
+ //
+
+ if (!TerminateThread(LPCThreadHandle,NO_ERROR)) {
+ ElfDbgPrint(("[ELF] LPC Thread termination failed %d\n",GetLastError()));
+ }
+ CloseHandle ( LPCThreadHandle );
+
+ return;
+}
+
+
+
+
+VOID
+FreeModuleAndLogFileStructs ( )
+
+/*++
+
+Routine Description:
+
+ This routine walks the module and log file list and frees all the
+ data structures.
+
+Arguments:
+
+ NONE
+
+Return Value:
+
+ NONE
+
+Note:
+
+ The file header and ditry bits must have been dealt with before
+ this routine is called. Also, the file must have been unmapped and
+ the handle closed.
+
+--*/
+{
+
+ NTSTATUS Status;
+ PLOGMODULE pModule;
+ PLOGFILE pLogFile;
+
+ ElfDbgPrint (("[ELF] Freeing module and log file structs\n"));
+
+ //
+ // First free all the modules
+ //
+
+ while (!IsListEmpty (&LogModuleHead) ) {
+
+ pModule = (PLOGMODULE)
+ CONTAINING_RECORD(LogModuleHead.Flink, LOGMODULE, ModuleList);
+
+ UnlinkLogModule(pModule); // Remove from linked list
+
+ ElfpFreeBuffer (pModule); // Free module memory
+
+ }
+
+ //
+ // Now free all the logfiles
+ //
+
+ while (!IsListEmpty (&LogFilesHead) ) {
+
+ pLogFile = (PLOGFILE)
+ CONTAINING_RECORD(LogFilesHead.Flink, LOGFILE, FileList);
+
+ Status = ElfpCloseLogFile ( pLogFile, ELF_LOG_CLOSE_NORMAL);
+
+ UnlinkLogFile(pLogFile); // Unlink the structure
+ RtlDeleteResource ( &pLogFile->Resource );
+ ElfpFreeBuffer (pLogFile->LogFileName);
+ ElfpFreeBuffer (pLogFile);
+ }
+}
+
+
+VOID
+ElfpCleanUp (
+ ULONG EventFlags
+ )
+
+/*++
+
+Routine Description:
+
+ This routine cleans up before the service terminates. It cleans up
+ based on the parameter passed in (which indicates what has been allocated
+ and/or started.
+
+Arguments:
+
+ Bit-mask indicating what needs to be cleaned up.
+
+Return Value:
+
+ NONE
+
+Note:
+ It is expected that the RegistryMonitor has already
+ been notified of Shutdown prior to calling this routine.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+
+ ElfDbgPrint (("[ELF] ElfpCleanUp.\n"));
+
+ //
+ // Notify the Service Controller for the first time that we are
+ // about to stop the service.
+ //
+ // *** STATUS UPDATE ***
+ ElfStatusUpdate(STOPPING);
+
+
+ //
+ // Stop the RPC Server
+ //
+ if (EventFlags & ELF_STARTED_RPC_SERVER) {
+ ElfDbgPrint (("[ELF] Stopping the RPC Server.\n"));
+
+ status = ElfGlobalData->StopRpcServer(eventlog_ServerIfHandle);
+ if (status != NO_ERROR) {
+ ElfDbgPrint (("[ELF] Stopping RpcServer Failed %d\n",status));
+ }
+ }
+
+ //
+ // Stop the LPC thread
+ //
+ if (EventFlags & ELF_STARTED_LPC_THREAD)
+ StopLPCThread();
+
+ //
+ // Tell service controller that we are making progress
+ //
+ // *** STATUS UPDATE ***
+ ElfStatusUpdate(STOPPING);
+
+ //
+ // Flush all the log files to disk.
+ //
+ ElfDbgPrint (("[ELF] Flushing Files.\n"));
+ ElfpFlushFiles();
+
+ //
+ // Tell service controller that we are making progress
+ //
+ ElfStatusUpdate(STOPPING);
+
+ //
+ // Clean up any resources that were allocated
+ //
+ FreeModuleAndLogFileStructs();
+
+ //
+ // Free up memory
+ //
+
+ if (LocalComputerName) {
+ ElfpFreeBuffer(LocalComputerName);
+ }
+
+ //
+ // If we queued up any events, flush them
+ //
+
+ ElfDbgPrint (("[ELF] Flushing QueuedEvents.\n"));
+ FlushQueuedEvents();
+
+ //
+ // Tell service controller of that we are making progress
+ //
+ ElfStatusUpdate(STOPPING);
+
+ if (EventFlags & ELF_INIT_GLOBAL_RESOURCE)
+ RtlDeleteResource ( &GlobalElfResource );
+
+ if (EventFlags & ELF_INIT_LOGHANDLE_CRIT_SEC)
+ RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)&LogHandleCritSec);
+
+ if (EventFlags & ELF_INIT_LOGFILE_CRIT_SEC)
+ RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)&LogFileCritSec);
+
+ if (EventFlags & ELF_INIT_QUEUED_EVENT_CRIT_SEC)
+ RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)&QueuedEventCritSec);
+
+ // *** STATUS UPDATE ***
+ ElfDbgPrint(("[ELF] Leaving the Eventlog service\n"));
+ ElfStatusUpdate(STOPPED);
+ ElCleanupStatus();
+ return;
+}
diff --git a/private/eventlog/test/buildreg.dat b/private/eventlog/test/buildreg.dat
new file mode 100644
index 000000000..9a74e65e5
--- /dev/null
+++ b/private/eventlog/test/buildreg.dat
@@ -0,0 +1,3 @@
+System,\systemroot\sysevent.log,4096,10
+Application,\systemroot\appevent.log,4096,10
+Security,\systemroot\audit.log,4096,10
diff --git a/private/eventlog/test/makefile b/private/eventlog/test/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/eventlog/test/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/eventlog/test/sources b/private/eventlog/test/sources
new file mode 100644
index 000000000..226e46d04
--- /dev/null
+++ b/private/eventlog/test/sources
@@ -0,0 +1,89 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Rajen Shah (rajens) 2-Jul-1991
+
+
+Revision History:
+
+!ENDIF
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+MAJORCOMP = eventlog
+MINORCOMP = test
+TARGETNAME= elftest
+
+#
+# The TARGETPATH and TARGETTYPE varialbes are defined by the developer.
+# The first specifies where the target is to be build. The second specifies
+# the type of target (either PROGRAM, DYNLINK or LIBRARY)
+#
+
+TARGETPATH=obj
+
+TARGETTYPE=PROGRAM
+
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib
+
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+INCLUDES=.;..;$(BASEDIR)\public\sdk\inc;$(BASEDIR)\private\net\inc
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+SOURCES= \
+ test.c
+
+
+C_DEFINES=-DNTOS2=1
+
+UMTYPE=console
+
+# The following builds the server executable file. The client is built
+# in the client directory.
+
+UMTYPE=console
+UMTEST=testwin*testwina
+UMLIBS=$(BASEDIR)\public\sdk\lib\*\kernel32.lib
+OPTIONAL_UMTEST=wintest
+
+#
+# Defining the NTTARGETFILES variable causes MAKEFILE.DEF to attempt to
+# include .\makefile.inc immediately after it specifies the top
+# level targets (all, clean and loc) and their dependencies. MAKEFILE.DEF
+# also expands the value of the NTTARGETFILES variable at the end of the
+# list of dependencies for the all target. Useful for specifying additional
+# targets and dependencies that don't fit the general case covered by
+# MAKEFILE.DEF
+#
diff --git a/private/eventlog/test/test.c b/private/eventlog/test/test.c
new file mode 100644
index 000000000..bb996bf8d
--- /dev/null
+++ b/private/eventlog/test/test.c
@@ -0,0 +1,1400 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ TEST.C
+
+Abstract:
+
+ Test program for the eventlog service. This program calls the Elf
+ APIs to test out the operation of the service.
+
+Author:
+
+ Rajen Shah (rajens) 05-Aug-1991
+
+Revision History:
+
+
+--*/
+/*----------------------*/
+/* INCLUDES */
+/*----------------------*/
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <stdio.h> // printf
+#include <string.h> // stricmp
+#include <stdlib.h>
+#include <process.h> // exit
+#include <elfcommn.h>
+#include <windows.h>
+#include <ntiolog.h>
+#include <malloc.h>
+
+#define READ_BUFFER_SIZE 1024*2 // Use 2K buffer
+
+#define SIZE_DATA_ARRAY 22
+
+#define SET_OPERATION(x) \
+ if (Operation != Invalid) { \
+ printf("Only one operation at a time\n"); \
+ Usage(); \
+ } \
+ Operation = x;
+
+//
+// Global buffer used to emulate "binary data" when writing an event
+// record.
+//
+ULONG Data[SIZE_DATA_ARRAY];
+enum _OPERATION_TYPE {
+ Invalid,
+ Clear,
+ Backup,
+ Read,
+ Write,
+ Notify,
+ LPC
+} Operation = Invalid;
+ULONG ReadFlags;
+BOOL Verbose = FALSE;
+ULONG NumberofRecords = 1;
+ULONG DelayInMilliseconds = 0;
+CHAR DefaultModuleName[] = "TESTAPP";
+PCHAR pModuleName = DefaultModuleName;
+PCHAR pBackupFileName;
+ANSI_STRING AnsiString;
+UNICODE_STRING ServerName;
+BOOL ReadingBackupFile = FALSE;
+BOOL ReadingModule = FALSE;
+BOOL WriteInvalidRecords = FALSE;
+BOOL InvalidUser = FALSE;
+
+// Function prototypes
+
+VOID ParseParms(ULONG argc, PCHAR *argv);
+
+VOID
+Initialize (
+ VOID
+ )
+{
+ ULONG i;
+
+ // Initialize the values in the data buffer.
+ //
+ for (i=0; i< SIZE_DATA_ARRAY; i++)
+ Data[i] = i;
+
+}
+
+
+VOID
+Usage (
+ VOID
+ )
+{
+ printf( "usage: \n" );
+ printf( "-c Clears the specified log\n");
+ printf( "-b <filename> Backs up the log to file <filename>\n");
+ printf( "-f <filename> Filename of backup log to use for read\n");
+ printf( "-i Generate invalid SID\n");
+ printf( "-l[i] nn Writes nn records thru LPC port [i ==> bad records]\n");
+ printf( "-m <modulename> Module name to use for read/clear\n");
+ printf( "-n Test out change notify\n");
+ printf( "-rsb Reads nn event log records sequentially backwards\n");
+ printf( "-rsf nn Reads nn event log records sequentially forwards\n");
+ printf( "-rrb <record> Reads event log from <record> backwards\n");
+ printf( "-rrf <record> Reads event log from <record> forwards\n");
+ printf( "-s <servername> Name of server to remote calls to\n");
+ printf( "-t nn Number of milliseconds to delay between read/write"
+ " (default 0)\n\tOnly used with -l switch\n");
+ printf( "-w <count> Writes <count> records\n");
+ exit(0);
+
+} // Usage
+
+
+NTSTATUS
+WriteLogEntry (
+ HANDLE LogHandle,
+ ULONG EventID
+ )
+{
+#define NUM_STRINGS 2
+#define SIZE_TOKEN_BUFFER 512
+
+ NTSTATUS Status;
+ USHORT EventType, i;
+ ULONG DataSize;
+ PSID pUserSid;
+ PWSTR Strings[NUM_STRINGS] = {L"StringOne", L"StringTwo"};
+ PUNICODE_STRING UStrings[NUM_STRINGS];
+ HANDLE hProcess;
+ HANDLE hToken;
+ PTOKEN_USER pTokenUser;
+ DWORD SizeRequired;
+
+ EventType = EVENTLOG_INFORMATION_TYPE;
+ DataSize = sizeof(ULONG) * SIZE_DATA_ARRAY;
+
+ //
+ // Get the SID of the current user (process)
+ //
+
+ pTokenUser = malloc(SIZE_TOKEN_BUFFER);
+
+ if (!InvalidUser) {
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
+ GetCurrentProcessId());
+ if (!hProcess) {
+ printf("Couldn't open the process, rc = %d\n", GetLastError());
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
+ printf("Couldn't open the token, rc = %d\n", GetLastError());
+ CloseHandle(hProcess);
+ return(STATUS_UNSUCCESSFUL);
+ }
+ if (!pTokenUser) {
+ printf("Couldn't allocate buffer for TokenUser\n");
+ CloseHandle(hToken);
+ CloseHandle(hProcess);
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ if (!GetTokenInformation(hToken, TokenUser, pTokenUser, SIZE_TOKEN_BUFFER,
+ &SizeRequired)) {
+ printf("Couldn't get TokenUser information, rc = %d\n",
+ GetLastError());
+ CloseHandle(hToken);
+ CloseHandle(hProcess);
+ free(pTokenUser);
+ return(STATUS_UNSUCCESSFUL);
+ }
+
+ CloseHandle(hToken);
+ CloseHandle(hProcess);
+ }
+ else {
+ memset(pTokenUser, 0xFADE, SIZE_TOKEN_BUFFER);
+ pTokenUser->User.Sid = (PSID)pUserSid;
+ }
+
+ pUserSid = pTokenUser->User.Sid;
+
+ for (i=0; i< SIZE_DATA_ARRAY; i++)
+ Data[i] += i;
+
+ // Allocate space for the unicode strings in the array, and
+ // copy over the strings from Strings[] to that array.
+ //
+ for (i=0; i<NUM_STRINGS; i++) {
+
+ UStrings[i] = malloc(sizeof(UNICODE_STRING));
+ RtlInitUnicodeString (UStrings[i], Strings[i]);
+ // force this to be non-null terminated
+ UStrings[i]->MaximumLength = UStrings[i]->Length;
+ }
+ Status = ElfReportEventW (
+ LogHandle,
+ EventType,
+ 0, // category
+ EventID,
+ pUserSid,
+ NUM_STRINGS,
+ DataSize,
+ UStrings,
+ (PVOID)Data,
+ 0, // Flags - paired event support
+ NULL, // RecordNumber | not in product 1
+ NULL // TimeWritten -
+ );
+
+ for (i=0; i<NUM_STRINGS; i++)
+ free(UStrings[i]);
+
+ free(pTokenUser);
+ return (Status);
+}
+
+
+VOID
+DisplayEventRecords( PVOID Buffer,
+ ULONG BufSize,
+ PULONG NumRecords)
+
+{
+ PEVENTLOGRECORD pLogRecord;
+ LPWSTR pwString;
+ ULONG Count = 0;
+ ULONG Offset = 0;
+ ULONG i;
+
+ pLogRecord = (PEVENTLOGRECORD) Buffer;
+
+ while (Offset < BufSize && Count < *NumRecords) {
+
+ printf("\nRecord # %lu\n", pLogRecord->RecordNumber);
+
+ printf("Length: 0x%lx TimeGenerated: 0x%lx EventID: 0x%lx EventType: 0x%x\n",
+ pLogRecord->Length, pLogRecord->TimeGenerated, pLogRecord->EventID,
+ pLogRecord->EventType);
+
+ printf("NumStrings: 0x%x StringOffset: 0x%lx UserSidLength: 0x%lx TimeWritten: 0x%lx\n",
+ pLogRecord->NumStrings, pLogRecord->StringOffset,
+ pLogRecord->UserSidLength, pLogRecord->TimeWritten);
+
+ printf("UserSidOffset: 0x%lx DataLength: 0x%lx DataOffset: 0x%lx Category: 0x%lx\n",
+ pLogRecord->UserSidOffset, pLogRecord->DataLength,
+ pLogRecord->DataOffset, pLogRecord->EventCategory);
+
+ //
+ // Print out module name
+ //
+
+ pwString = (PWSTR)((LPBYTE) pLogRecord + sizeof(EVENTLOGRECORD));
+ printf("ModuleName: %ws\n", pwString);
+
+ //
+ // Display ComputerName
+ //
+ pwString += wcslen(pwString) + 1;
+ printf("ComputerName: %ws\n", pwString);
+
+ //
+ // Display strings
+ //
+
+ pwString = (PWSTR)((LPBYTE)pLogRecord + pLogRecord->StringOffset);
+
+ printf("Strings: ");
+ for (i=0; i<pLogRecord->NumStrings; i++) {
+
+ printf(" %ws ", pwString);
+ pwString += wcslen(pwString) + 1;
+ }
+
+ printf("\n");
+
+ //
+ // If verbose mode, display binary data (up to 256 bytes)
+ // BUGBUG - this code will hit an alignment fault on mips.
+ //
+
+ if (Verbose) {
+ PULONG pData;
+ PULONG pEnd;
+
+ if (pLogRecord->DataLength < 80) {
+ pEnd = (PULONG)((PBYTE) pLogRecord + pLogRecord->DataOffset +
+ pLogRecord->DataLength);
+ }
+ else {
+ pEnd = (PULONG)((PBYTE) pLogRecord + pLogRecord->DataOffset +
+ 256);
+ }
+
+ printf("Data: \n\n");
+ for (pData = (PULONG)((PBYTE) pLogRecord + pLogRecord->DataOffset);
+ pData < pEnd; (PBYTE) pData += 32) {
+
+ printf("\t%08x %08x %08x %08x\n", pData[0], pData[1], pData[2],
+ pData[3]);
+ }
+ }
+
+ // Get next record
+ //
+ Offset += pLogRecord->Length;
+
+ pLogRecord = (PEVENTLOGRECORD)((ULONG)Buffer + Offset);
+
+ Count++;
+
+ }
+
+ *NumRecords = Count;
+
+}
+
+
+NTSTATUS
+ReadFromLog ( HANDLE LogHandle,
+ PVOID Buffer,
+ PULONG pBytesRead,
+ ULONG ReadFlag,
+ ULONG Record
+ )
+{
+ NTSTATUS Status;
+ ULONG MinBytesNeeded;
+
+ Status = ElfReadEventLogW (
+ LogHandle,
+ ReadFlag,
+ Record,
+ Buffer,
+ READ_BUFFER_SIZE,
+ pBytesRead,
+ &MinBytesNeeded
+ );
+
+
+ if (Status == STATUS_BUFFER_TOO_SMALL)
+ printf("Buffer too small. Need %lu bytes min\n", MinBytesNeeded);
+
+ return (Status);
+}
+
+
+NTSTATUS
+TestReadEventLog (
+ ULONG Count,
+ ULONG ReadFlag,
+ ULONG Record
+ )
+
+{
+ NTSTATUS Status, IStatus;
+
+ HANDLE LogHandle;
+ UNICODE_STRING ModuleNameU;
+ ANSI_STRING ModuleNameA;
+ ULONG NumRecords, BytesReturned;
+ PVOID Buffer;
+ ULONG RecordOffset;
+ ULONG NumberOfRecords;
+ ULONG OldestRecord;
+
+ printf("Testing ElfReadEventLog API to read %lu entries\n",Count);
+
+ Buffer = malloc (READ_BUFFER_SIZE);
+
+ //
+ // Initialize the strings
+ //
+ NumRecords = Count;
+ RtlInitAnsiString(&ModuleNameA, pModuleName);
+ RtlAnsiStringToUnicodeString(&ModuleNameU, &ModuleNameA, TRUE);
+ ModuleNameU.MaximumLength = ModuleNameU.Length;
+
+ //
+ // Open the log handle
+ //
+
+ if (ReadingBackupFile) {
+ printf("ElfOpenBackupEventLog - ");
+ Status = ElfOpenBackupEventLogW (
+ &ServerName,
+ &ModuleNameU,
+ &LogHandle
+ );
+ }
+ else {
+ printf("ElfOpenEventLog - ");
+ Status = ElfOpenEventLogW (
+ &ServerName,
+ &ModuleNameU,
+ &LogHandle
+ );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ printf("Error - 0x%lx\n", Status);
+
+ } else {
+ printf("SUCCESS\n");
+
+ //
+ // Get and print record information
+ //
+
+ Status = ElfNumberOfRecords(LogHandle, & NumberOfRecords);
+ if (NT_SUCCESS(Status)) {
+ Status = ElfOldestRecord(LogHandle, & OldestRecord);
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ printf("Query of record information failed with %X", Status);
+ return(Status);
+ }
+
+ printf("\nThere are %d records in the file, %d is the oldest"
+ " record number\n", NumberOfRecords, OldestRecord);
+
+ RecordOffset = Record;
+
+ while (Count && NT_SUCCESS(Status)) {
+
+ printf("Read %u records\n", NumRecords);
+ //
+ // Read from the log
+ //
+ Status = ReadFromLog ( LogHandle,
+ Buffer,
+ &BytesReturned,
+ ReadFlag,
+ RecordOffset
+ );
+ if (NT_SUCCESS(Status)) {
+
+ printf("Bytes read = 0x%lx\n", BytesReturned);
+ NumRecords = Count;
+ DisplayEventRecords(Buffer, BytesReturned, &NumRecords);
+ Count -= NumRecords;
+ }
+
+ }
+ printf("\n");
+
+ if (!NT_SUCCESS(Status)) {
+ if (Status == STATUS_END_OF_FILE) {
+ printf("Tried to read more records than in log file\n");
+ }
+ else {
+ printf ("Error - 0x%lx. Remaining count %lu\n", Status, Count);
+ }
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+ printf("Calling ElfCloseEventLog\n");
+ IStatus = ElfCloseEventLog (LogHandle);
+ }
+
+ return (Status);
+}
+
+
+NTSTATUS
+TestReportEvent (
+ ULONG Count
+ )
+
+{
+ NTSTATUS Status, IStatus;
+ HANDLE LogHandle;
+ UNICODE_STRING ModuleNameU;
+ ANSI_STRING ModuleNameA;
+ ULONG EventID = 99;
+
+ printf("Testing ElfReportEvent API\n");
+
+ //
+ // Initialize the strings
+ //
+
+ RtlInitAnsiString(&ModuleNameA, pModuleName);
+ RtlAnsiStringToUnicodeString(&ModuleNameU, &ModuleNameA, TRUE);
+ ModuleNameU.MaximumLength = ModuleNameU.Length;
+
+ //
+ // Open the log handle
+ //
+ printf("Calling ElfRegisterEventSource for WRITE %lu times - ", Count);
+ Status = ElfRegisterEventSourceW (
+ &ServerName,
+ &ModuleNameU,
+ &LogHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ printf("Error - 0x%lx\n", Status);
+
+ } else {
+ printf("SUCCESS\n");
+
+ while (Count && NT_SUCCESS(Status)) {
+
+ printf("Record # %u \n", Count);
+
+ //
+ // Write an entry into the log
+ //
+ Data[0] = Count; // Make data "unique"
+ EventID = (EventID + Count) % 100; // Vary the eventids
+ Status = WriteLogEntry ( LogHandle, EventID );
+ Count--;
+ }
+ printf("\n");
+
+ if (!NT_SUCCESS(Status)) {
+ if (Status == STATUS_LOG_FILE_FULL) {
+ printf("Log Full\n");
+ }
+ else {
+ printf ("Error - 0x%lx. Remaining count %lu\n", Status, Count);
+ }
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+ printf("Calling ElfDeregisterEventSource\n");
+ IStatus = ElfDeregisterEventSource (LogHandle);
+ }
+
+ return (Status);
+}
+
+
+NTSTATUS
+TestElfClearLogFile(
+ VOID
+ )
+
+{
+ NTSTATUS Status, IStatus;
+ HANDLE LogHandle;
+ UNICODE_STRING BackupU, ModuleNameU;
+ ANSI_STRING ModuleNameA;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE ClearHandle;
+ FILE_DISPOSITION_INFORMATION DeleteInfo = {TRUE};
+ IO_STATUS_BLOCK IoStatusBlock;
+ BOOLEAN DontRetry = FALSE;
+
+ printf("Testing ElfClearLogFile API\n");
+ //
+ // Initialize the strings
+ //
+ RtlInitAnsiString( &ModuleNameA, pModuleName);
+ RtlAnsiStringToUnicodeString(&ModuleNameU, &ModuleNameA, TRUE);
+ ModuleNameU.MaximumLength = ModuleNameU.Length ;
+
+ //
+ // Open the log handle
+ //
+ printf("Calling ElfOpenEventLog for CLEAR - ");
+ Status = ElfOpenEventLogW (
+ &ServerName,
+ &ModuleNameU,
+ &LogHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ printf("Error - 0x%lx\n", Status);
+
+ } else {
+ printf("SUCCESS\n");
+
+ //
+ // Clear the log file and back it up to "view.evt"
+ //
+
+ RtlInitUnicodeString( &BackupU,
+ L"\\SystemRoot\\System32\\Config\\view.evt" );
+ BackupU.MaximumLength = BackupU.Length;
+retry:
+ printf("Calling ElfClearEventLogFile backing up to view.evt ");
+ Status = ElfClearEventLogFileW (
+ LogHandle,
+ &BackupU
+ );
+
+ if (Status == STATUS_OBJECT_NAME_COLLISION) {
+ if (DontRetry) {
+ printf("Still can't backup to View.Evt\n");
+ }
+ else {
+ printf("Failed.\nView.Evt already exists, deleting ...\n");
+
+ //
+ // Open the file with delete access
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &BackupU,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenFile(&ClearHandle,
+ GENERIC_READ | DELETE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_DELETE,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ Status = NtSetInformationFile(
+ ClearHandle,
+ &IoStatusBlock,
+ &DeleteInfo,
+ sizeof(DeleteInfo),
+ FileDispositionInformation
+ );
+
+ if (NT_SUCCESS (Status) ) {
+ Status = NtClose (ClearHandle); // Discard status
+ goto retry;
+ }
+
+ printf("Delete failed 0x%lx\n",Status);
+ Status = NtClose (ClearHandle); // Discard status
+ goto JustClear;
+ }
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ printf ("Error - 0x%lx\n", Status);
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+JustClear:
+
+ //
+ // Now just clear the file without backing it up
+ //
+ printf("Calling ElfClearEventLogFile with no backup ");
+ Status = ElfClearEventLogFileW (
+ LogHandle,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ printf ("Error - 0x%lx\n", Status);
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+ printf("Calling ElfCloseEventLog\n");
+ IStatus = ElfCloseEventLog (LogHandle);
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+TestElfBackupLogFile(
+ VOID
+ )
+
+{
+ NTSTATUS Status, IStatus;
+ HANDLE LogHandle;
+ UNICODE_STRING BackupU, ModuleNameU;
+ ANSI_STRING AnsiString;
+
+ printf("Testing ElfBackupLogFile API\n");
+
+ //
+ // Initialize the strings
+ //
+
+ RtlInitAnsiString( &AnsiString, pModuleName);
+ RtlAnsiStringToUnicodeString(&ModuleNameU, &AnsiString, TRUE);
+ ModuleNameU.MaximumLength = ModuleNameU.Length ;
+
+ //
+ // Open the log handle
+ //
+
+ printf("Calling ElfOpenEventLog for BACKUP - ");
+ Status = ElfOpenEventLogW (
+ &ServerName,
+ &ModuleNameU,
+ &LogHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ printf("Error - 0x%lx\n", Status);
+
+ } else {
+ printf("SUCCESS\n");
+
+ //
+ // Backup the log file
+ //
+
+ printf("Calling ElfBackupEventLogFile backing up to %s ",
+ pBackupFileName);
+
+ RtlInitAnsiString( &AnsiString, pBackupFileName);
+ RtlAnsiStringToUnicodeString(&BackupU, &AnsiString, TRUE);
+ BackupU.MaximumLength = BackupU.Length;
+
+ Status = ElfBackupEventLogFileW (
+ LogHandle,
+ &BackupU
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ printf ("Error - 0x%lx\n", Status);
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+
+ printf("Calling ElfCloseEventLog - ");
+ IStatus = ElfCloseEventLog (LogHandle);
+ if (NT_SUCCESS(IStatus)) {
+ printf("Success\n");
+ }
+ else {
+ printf("Failed with code %X\n", IStatus);
+ }
+ }
+
+ return(Status);
+}
+
+#define DRIVER_NAME L"FLOPPY"
+#define DEVICE_NAME L"A:"
+#define STRING L"Test String"
+
+// These include the NULL terminator, but is length in chars, not bytes
+#define DRIVER_NAME_LENGTH 7
+#define DEVICE_NAME_LENGTH 3
+#define STRING_LENGTH 12
+
+#define NUMBER_OF_DATA_BYTES 8
+
+VOID
+TestLPCWrite(
+ DWORD NumberOfRecords,
+ DWORD MillisecondsToDelay
+ )
+{
+
+ HANDLE PortHandle;
+ UNICODE_STRING PortName;
+ NTSTATUS Status;
+ SECURITY_QUALITY_OF_SERVICE Qos;
+ PIO_ERROR_LOG_MESSAGE pIoErrorLogMessage;
+ DWORD i;
+ LPWSTR pDestinationString;
+ PPORT_MESSAGE RequestMessage;
+ PORT_MESSAGE ReplyMessage;
+ WORD DataLength;
+ WORD TotalLength;
+ INT YorN;
+ CHAR NumberString[8];
+ ULONG MessageId = 1;
+ DWORD BadType = 0;
+
+ //
+ // Warn the user about how this test works
+ //
+
+ printf("\nThis test doesn't end! It will write a number of\n"
+ "records, then prompt you to write more. This is \n"
+ "required since it is simulating the system thread\n"
+ "which never shuts down it's connection\n\n"
+ "Do you wish to continue with this test (y or n)? ");
+
+ YorN = getc(stdin);
+
+ if (YorN == 'n' || YorN == 'N') {
+ return;
+ }
+
+ //
+ // Initialize the SecurityQualityofService structure
+ //
+
+ Qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ Qos.ImpersonationLevel = SecurityImpersonation;
+ Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ Qos.EffectiveOnly = TRUE;
+
+ //
+ // Connect to the LPC Port
+ //
+
+ RtlInitUnicodeString( &PortName, L"\\ErrorLogPort" );
+
+ Status = NtConnectPort(& PortHandle,
+ & PortName,
+ & Qos,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ printf("Connect to the LPC port failed with RC %X\n", Status);
+ return;
+ }
+
+ //
+ // Allocate the memory for the Message to send to the LPC port. It
+ // will contain a PORT_MESSAGE followed by an IO_ERROR_LOG_MESSAGE
+ // followed by Drivername and Devicename UNICODE strings
+ //
+
+ DataLength = PORT_MAXIMUM_MESSAGE_LENGTH -
+ (sizeof(IO_ERROR_LOG_MESSAGE)
+ + DRIVER_NAME_LENGTH * sizeof(WCHAR)
+ + DEVICE_NAME_LENGTH * sizeof(WCHAR)
+ + STRING_LENGTH * sizeof(WCHAR));
+ TotalLength = PORT_MAXIMUM_MESSAGE_LENGTH + (WORD) sizeof(PORT_MESSAGE);
+
+ RequestMessage = (PPORT_MESSAGE) malloc(TotalLength);
+ if (RequestMessage == NULL) {
+ printf("Couldn't alloc %d bytes of memory for message\n", TotalLength);
+ NtClose(PortHandle);
+ return;
+ }
+
+ pIoErrorLogMessage = (PIO_ERROR_LOG_MESSAGE) ((LPBYTE) RequestMessage +
+ sizeof(PORT_MESSAGE));
+
+ //
+ // Initialize the PORT_MESSAGE
+ //
+
+ RequestMessage->u1.s1.DataLength = PORT_MAXIMUM_MESSAGE_LENGTH;
+ RequestMessage->u1.s1.TotalLength = TotalLength;
+ RequestMessage->u2.s2.Type = 0;
+ RequestMessage->u2.ZeroInit = 0;
+ RequestMessage->ClientId.UniqueProcess = GetCurrentProcess();
+ RequestMessage->ClientId.UniqueThread = GetCurrentThread();
+ RequestMessage->MessageId = 0x1234;
+
+ //
+ // Initialize the IO_ERROR_LOG_MESSAGE
+ //
+
+ pIoErrorLogMessage->Type = IO_TYPE_ERROR_MESSAGE;
+ pIoErrorLogMessage->Size = PORT_MAXIMUM_MESSAGE_LENGTH;
+ pIoErrorLogMessage->DriverNameLength = DRIVER_NAME_LENGTH * sizeof(WCHAR);
+ NtQuerySystemTime((PTIME) &pIoErrorLogMessage->TimeStamp);
+ pIoErrorLogMessage->DriverNameOffset = sizeof(IO_ERROR_LOG_MESSAGE) +
+ DataLength - sizeof(DWORD);
+
+ pIoErrorLogMessage->EntryData.MajorFunctionCode = 1;
+ pIoErrorLogMessage->EntryData.RetryCount = 5;
+ pIoErrorLogMessage->EntryData.DumpDataSize = DataLength;
+ pIoErrorLogMessage->EntryData.NumberOfStrings = 2;
+ pIoErrorLogMessage->EntryData.StringOffset = sizeof(IO_ERROR_LOG_MESSAGE)
+ - sizeof(DWORD) + DataLength +
+ DRIVER_NAME_LENGTH * sizeof(WCHAR);
+ pIoErrorLogMessage->EntryData.EventCategory = 0;
+ pIoErrorLogMessage->EntryData.ErrorCode = 0xC0020008;
+ pIoErrorLogMessage->EntryData.UniqueErrorValue = 0x20008;
+ pIoErrorLogMessage->EntryData.FinalStatus = 0x1111;
+ pIoErrorLogMessage->EntryData.SequenceNumber = 1;
+ pIoErrorLogMessage->EntryData.IoControlCode = 0xFF;
+ pIoErrorLogMessage->EntryData.DeviceOffset =
+ RtlConvertUlongToLargeInteger(1);
+
+ for (i = 0; i < DataLength ; i++ ) {
+ pIoErrorLogMessage->EntryData.DumpData[i] = i;
+ }
+
+ //
+ // Copy the strings
+ //
+
+ pDestinationString = (LPWSTR) ((LPBYTE) pIoErrorLogMessage
+ + sizeof(IO_ERROR_LOG_MESSAGE)
+ - sizeof(DWORD) + pIoErrorLogMessage->EntryData.DumpDataSize);
+ wcscpy(pDestinationString, DRIVER_NAME);
+
+ pDestinationString += DRIVER_NAME_LENGTH;
+ wcscpy(pDestinationString, DEVICE_NAME);
+
+ pDestinationString += DEVICE_NAME_LENGTH;
+ wcscpy(pDestinationString, STRING);
+
+ //
+ // Write the packet as many times as requested, with delay, then ask
+ // if they want to write more
+ //
+ while (NumberOfRecords) {
+
+ printf("\n\nWriting %d records\n", NumberOfRecords);
+
+ while(NumberOfRecords--) {
+ printf(".");
+
+ //
+ // Put in a unique message number
+ //
+
+ RequestMessage->MessageId = MessageId++;
+
+ //
+ // If they want invalid records, give them invalid records
+ //
+
+ if (WriteInvalidRecords) {
+ switch (BadType++) {
+ case 0:
+ pIoErrorLogMessage->EntryData.DumpDataSize++;
+ break;
+
+ case 1:
+ pIoErrorLogMessage->EntryData.NumberOfStrings++;
+ break;
+
+ case 2:
+ pIoErrorLogMessage->EntryData.StringOffset++;
+ break;
+
+ default:
+ BadType = 0;
+ }
+ }
+
+ Status = NtRequestWaitReplyPort(PortHandle,
+ RequestMessage,
+ & ReplyMessage);
+
+ if (!NT_SUCCESS(Status)) {
+ printf("Request to LPC port failed with RC %X\n", Status);
+ break;
+ }
+
+ //
+ // Delay a little bit, if requested
+ //
+
+ if (MillisecondsToDelay) {
+ Sleep(MillisecondsToDelay);
+ }
+ }
+ printf("\nEnter the number of records to write ");
+
+ while (!gets(NumberString) || !(NumberOfRecords = atoi(NumberString))) {
+ printf("Enter the number of records to write ");
+ }
+ }
+
+ //
+ // Clean up and exit
+ //
+
+ Status = NtClose(PortHandle);
+ if (!NT_SUCCESS(Status)) {
+ printf("Close of Port failed with RC %X\n", Status);
+ }
+
+ free(RequestMessage);
+
+ return;
+
+}
+
+
+VOID
+TestChangeNotify(
+ VOID
+ )
+{
+
+ HANDLE Event;
+ UNICODE_STRING ModuleNameU;
+ ANSI_STRING ModuleNameA;
+ NTSTATUS Status;
+ HANDLE LogHandle;
+ OBJECT_ATTRIBUTES obja;
+ ULONG NumRecords;
+ ULONG BytesRead;
+ ULONG MinBytesNeeded;
+ PVOID Buffer;
+ ULONG OldestRecord;
+ ULONG NumberOfRecords;
+
+ RtlInitAnsiString(&ModuleNameA, pModuleName);
+ RtlAnsiStringToUnicodeString(&ModuleNameU, &ModuleNameA, TRUE);
+ ModuleNameU.MaximumLength = ModuleNameU.Length ;
+
+ Buffer = malloc (READ_BUFFER_SIZE);
+ ASSERT(Buffer);
+
+ //
+ // Open the log handle
+ //
+
+ printf("ElfOpenEventLog - ");
+ Status = ElfOpenEventLogW (
+ &ServerName,
+ &ModuleNameU,
+ &LogHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ printf("Error - 0x%lx\n", Status);
+ return;
+ }
+
+ printf("SUCCESS\n");
+
+ //
+ // Create the Event
+ //
+
+ InitializeObjectAttributes( &obja, NULL, 0, NULL, NULL);
+
+ Status = NtCreateEvent(
+ &Event,
+ SYNCHRONIZE | EVENT_QUERY_STATE | EVENT_MODIFY_STATE,
+ &obja,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Get the read pointer to the end of the log
+ //
+
+ Status = ElfOldestRecord(LogHandle, & OldestRecord);
+ ASSERT(NT_SUCCESS(Status));
+ Status = ElfNumberOfRecords(LogHandle, & NumberOfRecords);
+ ASSERT(NT_SUCCESS(Status));
+ OldestRecord += NumberOfRecords - 1;
+
+ Status = ElfReadEventLogW (
+ LogHandle,
+ EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ,
+ OldestRecord,
+ Buffer,
+ READ_BUFFER_SIZE,
+ &BytesRead,
+ &MinBytesNeeded
+ );
+
+
+ //
+ // This one should hit end of file
+ //
+
+ Status = ElfReadEventLogW (
+ LogHandle,
+ EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
+ 0,
+ Buffer,
+ READ_BUFFER_SIZE,
+ &BytesRead,
+ &MinBytesNeeded
+ );
+
+ if (Status != STATUS_END_OF_FILE) {
+ printf("Hmmm, should have hit EOF (unless there are writes going"
+ " on elsewhere- %X\n", Status);
+ }
+
+ //
+ // Call ElfChangeNotify
+ //
+
+ Status = ElfChangeNotify(LogHandle, Event);
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Now loop waiting for the event to get toggled
+ //
+
+ while (1) {
+
+ Status = NtWaitForSingleObject(Event, FALSE, 0);
+ printf("The change notify event just got kicked\n");
+
+ //
+ // Now read the new records
+ //
+
+ while(1) {
+
+ Status = ElfReadEventLogW (
+ LogHandle,
+ EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
+ 0,
+ Buffer,
+ READ_BUFFER_SIZE,
+ &BytesRead,
+ &MinBytesNeeded
+ );
+
+ if (Status == STATUS_END_OF_FILE) {
+ break;
+ }
+
+ NumRecords = 0xffff; // should be plenty
+ DisplayEventRecords (Buffer, BytesRead, &NumRecords);
+ }
+ }
+}
+
+VOID
+_CRTAPI1
+main (
+ IN SHORT argc,
+ IN PSZ argv[]
+ )
+{
+
+ Initialize(); // Init any data
+
+ //
+ // Parse the command line
+ //
+
+ ParseParms(argc, argv);
+
+ switch (Operation) {
+ case Clear:
+
+ TestElfClearLogFile();
+ break;
+
+ case Backup:
+
+ TestElfBackupLogFile();
+ break;
+
+ case Read:
+
+ if (ReadFlags & EVENTLOG_SEEK_READ) {
+ TestReadEventLog(1, ReadFlags, NumberofRecords) ;
+ }
+ else {
+ TestReadEventLog(NumberofRecords, ReadFlags, 0) ;
+ }
+ break;
+
+ case Write:
+
+ TestReportEvent (NumberofRecords);
+ break;
+
+ case LPC:
+ TestLPCWrite(NumberofRecords, DelayInMilliseconds);
+ break;
+
+ case Notify:
+ TestChangeNotify();
+ break;
+
+ default:
+ printf("Invalid switch from ParseParms %d\n", Operation);
+ }
+}
+
+
+VOID
+ParseParms(
+ ULONG argc,
+ PCHAR *argv
+ )
+{
+
+ ULONG i;
+ PCHAR pch;
+
+ for (i = 1; i < argc; i++) { /* for each argument */
+ if (*(pch = argv[i]) == '-') {
+ while (*++pch) {
+ switch (*pch) {
+ case 'b':
+
+ SET_OPERATION(Backup)
+
+ //
+ // Get the file name for backup
+ //
+
+ if (i+1 < argc) {
+ pBackupFileName = argv[++i];
+ }
+ else {
+ Usage();
+ }
+ break;
+
+ case 'c':
+
+ SET_OPERATION(Clear)
+
+ break;
+
+ case 'f':
+ if (i+1 < argc) {
+ pModuleName = argv[++i];
+ ReadingBackupFile = TRUE;
+ }
+ else {
+ Usage();
+ }
+ break;
+
+ case '?':
+ case 'h':
+ case 'H':
+ Usage();
+ break;
+
+ case 'i':
+ InvalidUser = TRUE;
+ break;
+
+ case 'l':
+
+ SET_OPERATION(LPC);
+
+ //
+ // See if they want invalid records
+ //
+
+ if (*++pch == 'i') {
+ WriteInvalidRecords = TRUE;
+ }
+
+ //
+ // See if they specified a number of records
+ //
+
+ if (i + 1 < argc && argv[i+1][0] != '-') {
+ NumberofRecords = atoi(argv[++i]);
+ if (NumberofRecords == 0) {
+ Usage();
+ }
+ }
+
+ break;
+
+ case 'm':
+ if (i+1 < argc) {
+ pModuleName = argv[++i];
+ ReadingModule = TRUE;
+ }
+ else {
+ Usage();
+ }
+ break;
+
+ case 'n':
+ SET_OPERATION(Notify)
+ break;
+
+ case 'r':
+
+ SET_OPERATION(Read)
+
+ //
+ // Different Read options
+ //
+
+ if (*++pch == 's') {
+ ReadFlags |= EVENTLOG_SEQUENTIAL_READ;
+ }
+ else if (*pch == 'r') {
+ ReadFlags |= EVENTLOG_SEEK_READ;
+ }
+ else {
+ Usage();
+ }
+
+ if (*++pch == 'f') {
+ ReadFlags |= EVENTLOG_FORWARDS_READ;
+ }
+ else if (*pch == 'b') {
+ ReadFlags |= EVENTLOG_BACKWARDS_READ;
+ }
+ else {
+ Usage();
+ }
+
+ //
+ // See if they specified a number of records
+ //
+
+ if (i + 1 < argc && argv[i+1][0] != '-') {
+ NumberofRecords = atoi(argv[++i]);
+ if (NumberofRecords == 0) {
+ Usage();
+ }
+ }
+
+ break;
+
+ case 's':
+ if (i+1 >= argc) {
+ printf("Must supply a server name with -s\n");
+ Usage();
+ }
+ RtlInitAnsiString(&AnsiString, argv[++i]);
+ RtlAnsiStringToUnicodeString(&ServerName, &AnsiString,
+ TRUE);
+ break;
+
+ case 't':
+ DelayInMilliseconds = atoi(argv[++i]);
+ break;
+
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'w':
+
+ SET_OPERATION(Write)
+
+ //
+ // See if they specified a number of records
+ //
+
+ if (i + 1 < argc && argv[i+1][0] != '-') {
+ NumberofRecords = atoi(argv[++i]);
+ if (NumberofRecords == 0) {
+ Usage();
+ }
+ }
+
+ break;
+
+ default: /* Invalid options */
+ printf("Invalid option %c\n\n", *pch);
+ Usage();
+ break;
+ }
+ }
+ }
+
+ //
+ // There aren't any non switch parms
+ //
+
+ else {
+ Usage();
+ }
+ }
+
+ //
+ // Verify parms are correct
+ //
+
+
+ if ( Operation == Invalid) {
+ printf( "Must specify an operation\n");
+ Usage( );
+ }
+
+ if (ReadingBackupFile && ReadingModule) {
+ printf("-m and -f are mutually exclusive\n");
+ Usage();
+ }
+
+ if (ReadingBackupFile && Operation == Write) {
+ printf("You cannot write to a backup log file\n");
+ Usage();
+ }
+ if (DelayInMilliseconds && Operation != LPC) {
+ printf("\n\n-t switch is only used with -l\n\n");
+ }
+}
+
diff --git a/private/eventlog/test/testwin.c b/private/eventlog/test/testwin.c
new file mode 100644
index 000000000..bea524159
--- /dev/null
+++ b/private/eventlog/test/testwin.c
@@ -0,0 +1,770 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ TESTWIN.C
+
+Abstract:
+
+ Test program for the eventlog service. This program calls the Win
+ APIs to test out the operation of the service.
+
+Author:
+
+ Rajen Shah (rajens) 05-Aug-1991
+
+Revision History:
+
+
+--*/
+/*----------------------*/
+/* INCLUDES */
+/*----------------------*/
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <stdio.h> // printf
+#include <string.h> // stricmp
+#include <stdlib.h>
+#include <windows.h>
+#include <netevent.h>
+
+//
+// Turn on NotifyChangeEventLog
+//
+#define TEST_NOTIFY 1
+//#define TEST_REMOTE 1
+
+#define READ_BUFFER_SIZE 1024*2 // Use 2K buffer
+
+#define SIZE_DATA_ARRAY 65
+
+//
+// Global buffer used to emulate "binary data" when writing an event
+// record.
+//
+ DWORD Data[SIZE_DATA_ARRAY];
+// LPWSTR ServerName=L"\\\\danl2";
+ LPWSTR ServerName=NULL;
+
+
+VOID
+Initialize (
+ VOID
+ )
+{
+ DWORD i;
+
+ // Initialize the values in the data buffer.
+ //
+ for (i=0; i< SIZE_DATA_ARRAY; i++)
+ Data[i] = i;
+
+}
+
+
+BOOL
+Usage (
+ VOID
+ )
+{
+ printf( "usage: \n" );
+ printf( "-b <filename> Tests BackupEventLog API\n");
+ printf( "-c Tests ClearEventLog API\n");
+ printf( "-n Tests NotifyChangeEventlog\n");
+ printf( "-rsb Reads event log sequentially backwards\n");
+ printf( "-rsf Reads event log sequentially forwards\n");
+ printf( "-rrb <record> Reads event log from <record> backwards\n");
+ printf( "-rrf <record> Reads event log from <record> forwards\n");
+ printf( "-w <count> Tests ReportEvent API <count> times\n");
+ return ERROR_INVALID_PARAMETER;
+
+} // Usage
+
+
+
+BOOL
+WriteLogEntry ( HANDLE LogHandle, DWORD EventID )
+
+{
+#define NUM_STRINGS 2
+
+ BOOL Status;
+ WORD EventType, i;
+ DWORD DataSize;
+ PSID pUserSid;
+
+ PWSTR Strings[NUM_STRINGS] = {L"StringAOne",
+ L"StringATwo"
+ };
+
+ EventType = EVENTLOG_INFORMATION_TYPE;
+ pUserSid = NULL;
+ DataSize = sizeof(DWORD) * SIZE_DATA_ARRAY;
+
+ for (i=0; i< SIZE_DATA_ARRAY; i++)
+ Data[i] += i;
+
+ Status = ReportEventW (
+ LogHandle,
+ EventType,
+ 0, // event category
+ EventID,
+ pUserSid,
+ NUM_STRINGS,
+ DataSize,
+ Strings,
+ (PVOID)Data
+ );
+
+ return (Status);
+}
+
+DWORD
+WriteLogEntryMsg ( HANDLE LogHandle, DWORD EventID )
+/*
+ This function requires a registry entry in the Applications section
+ of the Eventlog for TESTWINAPP, it will use the netevent.dll message file.
+*/
+
+{
+#define NUM_STRINGS 2
+
+ WORD EventType;
+ DWORD DataSize;
+ PSID pUserSid;
+
+ PWSTR Strings[NUM_STRINGS];
+
+ Strings[0] = L"This is a BOGUS message for TEST purposes Ignore this substitution text";
+ Strings[1] = L"GHOST SERVICE in the long string format - I wanted a long string to pass into this function";
+
+ EventType = EVENTLOG_INFORMATION_TYPE;
+ pUserSid = NULL;
+ DataSize = sizeof(DWORD) * SIZE_DATA_ARRAY;
+
+
+ if (!ReportEventW (
+ LogHandle,
+ EventType,
+ 0, // event category
+ EVENT_SERVICE_START_FAILED_NONE,
+ pUserSid,
+ NUM_STRINGS,
+ 0, // DataSize
+ Strings,
+ (PVOID)NULL // Data
+ )) {
+
+ printf("ReportEventW failed %d\n",GetLastError());
+ return(GetLastError());
+ }
+ return (NO_ERROR);
+}
+
+
+VOID
+DisplayEventRecords( PVOID Buffer,
+ DWORD BufSize,
+ ULONG *NumRecords)
+
+{
+ PEVENTLOGRECORD pLogRecord;
+ ANSI_STRING StringA;
+ UNICODE_STRING StringU;
+ PWSTR pwString;
+ DWORD Count = 0;
+ DWORD Offset = 0;
+ DWORD i;
+
+ pLogRecord = (PEVENTLOGRECORD) Buffer;
+
+ while ((DWORD)Offset < BufSize) {
+
+ printf("\nRecord # %lu\n", ++Count);
+
+ printf("Length: 0x%lx TimeGenerated: 0x%lx EventID: 0x%lx EventType: 0x%x\n",
+ pLogRecord->Length, pLogRecord->TimeGenerated, pLogRecord->EventID,
+ pLogRecord->EventType);
+
+ printf("NumStrings: 0x%x StringOffset: 0x%lx UserSidLength: 0x%lx TimeWritten: 0x%lx\n",
+ pLogRecord->NumStrings, pLogRecord->StringOffset,
+ pLogRecord->UserSidLength, pLogRecord->TimeWritten);
+
+ printf("UserSidOffset: 0x%lx DataLength: 0x%lx DataOffset: 0x%lx \n",
+ pLogRecord->UserSidOffset, pLogRecord->DataLength,
+ pLogRecord->DataOffset);
+
+ //
+ // Print out module name
+ //
+ pwString = (PWSTR)((DWORD)pLogRecord + sizeof(EVENTLOGRECORD));
+ RtlInitUnicodeString (&StringU, pwString);
+ RtlUnicodeStringToAnsiString (&StringA, &StringU, TRUE);
+
+ printf("ModuleName: %s ", StringA.Buffer);
+ RtlFreeAnsiString (&StringA);
+
+ //
+ // Display ComputerName
+ //
+ pwString = pwString + (wcslen(pwString) + 1);
+
+ RtlInitUnicodeString (&StringU, pwString);
+ RtlUnicodeStringToAnsiString (&StringA, &StringU, TRUE);
+
+ printf("ComputerName: %s\n",StringA.Buffer);
+ RtlFreeAnsiString (&StringA);
+
+ //
+ // Display strings
+ //
+ pwString = (PWSTR)((DWORD)Buffer + pLogRecord->StringOffset);
+
+ printf("\nStrings: \n");
+ for (i=0; i<pLogRecord->NumStrings; i++) {
+
+ RtlInitUnicodeString (&StringU, pwString);
+ RtlUnicodeStringToAnsiString (&StringA, &StringU, TRUE);
+
+ printf(" %s \n",StringA.Buffer);
+
+ RtlFreeAnsiString (&StringA);
+
+ pwString = (PWSTR)((DWORD)pwString + StringU.MaximumLength);
+ }
+
+ // Get next record
+ //
+ Offset += pLogRecord->Length;
+
+ pLogRecord = (PEVENTLOGRECORD)((DWORD)Buffer + Offset);
+
+ }
+ *NumRecords = Count;
+
+}
+
+
+BOOL
+ReadFromLog ( HANDLE LogHandle,
+ PVOID Buffer,
+ ULONG *pBytesRead,
+ DWORD ReadFlag,
+ DWORD Record
+ )
+{
+ BOOL Status;
+ DWORD MinBytesNeeded;
+ DWORD ErrorCode;
+
+ Status = ReadEventLogW (
+ LogHandle,
+ ReadFlag,
+ Record,
+ Buffer,
+ READ_BUFFER_SIZE,
+ pBytesRead,
+ &MinBytesNeeded
+ );
+
+
+ if (!Status) {
+ ErrorCode = GetLastError();
+ if (ErrorCode == ERROR_HANDLE_EOF) {
+ Status = TRUE;
+ }
+ else if (ErrorCode == ERROR_NO_MORE_FILES) {
+ printf("Buffer too small. Need %lu bytes min\n", MinBytesNeeded);
+ }
+ else {
+ printf("Error from ReadEventLog %d \n", ErrorCode);
+ }
+
+ }
+
+ return (Status);
+}
+
+
+
+
+BOOL
+TestReadEventLog (DWORD Count, DWORD ReadFlag, DWORD Record)
+
+{
+ BOOL bStatus,IStatus;
+ DWORD status;
+ HANDLE LogHandle;
+ LPWSTR ModuleName;
+ DWORD NumRecords, BytesReturned;
+ PVOID Buffer;
+ DWORD RecordOffset;
+ DWORD NumberOfRecords;
+ DWORD OldestRecord;
+
+ printf("Testing ReadEventLog API to read %lu entries\n",Count);
+
+ Buffer = malloc (READ_BUFFER_SIZE);
+
+ //
+ // Initialize the strings
+ //
+ NumRecords = Count;
+ ModuleName = L"TESTWINAPP";
+
+ //
+ // Open the log handle
+ //
+ printf("OpenEventLog - ");
+ LogHandle = OpenEventLogW (
+ ServerName,
+ ModuleName
+ );
+
+ if (LogHandle == NULL) {
+ printf("Error - %d\n", GetLastError());
+
+ } else {
+ printf("SUCCESS\n");
+
+ //
+ // Get and print record information
+ //
+
+ bStatus = GetNumberOfEventLogRecords(LogHandle, & NumberOfRecords);
+ if (bStatus) {
+ bStatus = GetOldestEventLogRecord(LogHandle, & OldestRecord);
+ }
+
+ if (!bStatus) {
+ printf("Query of record information failed with %X", GetLastError());
+ return(bStatus);
+ }
+
+ printf("\nThere are %d records in the file, %d is the oldest"
+ " record number\n", NumberOfRecords, OldestRecord);
+
+ RecordOffset = Record;
+
+ printf("Reading %u records\r", Count);
+
+ while (Count) {
+
+ //
+ // Read from the log
+ //
+ bStatus = ReadFromLog ( LogHandle,
+ Buffer,
+ &BytesReturned,
+ ReadFlag,
+ RecordOffset
+ );
+ if (bStatus) {
+ printf("Bytes read = 0x%lx\n", BytesReturned);
+ printf("Read %u records\n", NumRecords);
+ DisplayEventRecords(Buffer, BytesReturned, &NumRecords);
+ Count -= NumRecords;
+ RecordOffset += NumRecords;
+ } else {
+ break;
+ }
+
+ if (BytesReturned == 0)
+ break;
+ }
+ printf("\n");
+
+ if (!bStatus) {
+ printf ("ReadFromLog Error - %d. Remaining count %lu\n", GetLastError(),
+ Count);
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+ printf("Calling CloseEventLog\n");
+ IStatus = CloseEventLog (LogHandle);
+ }
+
+ return (bStatus);
+}
+
+
+
+BOOL
+TestWriteEventLog (DWORD Count)
+
+{
+ DWORD Status=NO_ERROR;
+ BOOL IStatus;
+ HANDLE LogHandle;
+ LPWSTR ModuleName;
+ DWORD EventID = 99;
+ DWORD WriteCount;
+ DWORD DataNum=0;
+
+ printf("Testing ReportEvent API\n");
+
+ //
+ // Initialize the strings
+ //
+ ModuleName = L"TESTWINAPP";
+
+ printf("Calling RegisterEventSource for WRITE %lu times\n", Count);
+ while ((Count > 0) && (Status== NO_ERROR)) {
+ //
+ // Open the log handle
+ //
+ LogHandle = RegisterEventSourceW (
+ ServerName,
+ ModuleName
+ );
+
+ if (LogHandle == NULL) {
+ Status = GetLastError();
+ printf("RegisterEventSource Failure - %d\n", Status);
+ return(Status);
+
+ } else {
+ printf("Registered - ");
+ WriteCount = 5;
+ printf("Record # %u: ", Count);
+
+ while ((WriteCount>0) && (Status==NO_ERROR)) {
+
+ //
+ // Write an entry into the log
+ //
+ Data[0] = DataNum; // Make data "unique"
+ EventID = (EventID + DataNum) % 100; // Vary the eventids
+ Status = WriteLogEntryMsg( LogHandle, EventID );
+ DataNum++;
+ WriteCount--;
+
+ if (Status != NO_ERROR) {
+ printf ("WriteLogEntry Error - %d. Remaining count %lu\n",Status,Count);
+ } else {
+ printf ("%d,",WriteCount);
+ }
+ }
+
+ IStatus = DeregisterEventSource (LogHandle);
+ printf(" - Deregistered\n");
+ }
+ Count--;
+ }
+
+ printf("\n");
+ return (Status);
+}
+
+
+
+BOOL
+TestClearLogFile ()
+
+{
+ BOOL Status, IStatus;
+ HANDLE LogHandle;
+ LPWSTR ModuleName, BackupName;
+
+ printf("Testing ClearLogFile API\n");
+ //
+ // Initialize the strings
+ //
+ ModuleName = L"TESTWINAPP";
+
+ //
+ // Open the log handle
+ //
+ printf("Calling OpenEventLog for CLEAR - ");
+ LogHandle = OpenEventLogW (
+ NULL,
+ ModuleName
+ );
+
+ if (LogHandle == NULL) {
+ printf("OpenEventLog Error - %d\n", GetLastError());
+
+ } else {
+ printf("SUCCESS\n");
+
+ //
+ // Clear the log file and back it up to "view.log"
+ //
+
+ printf("Calling ClearEventLog backing up to view.log ");
+ BackupName = L"\\\\danhi386\\roote\\view.log";
+
+ Status = ClearEventLogW (
+ LogHandle,
+ BackupName
+ );
+
+ if (!Status) {
+ printf ("ClearEventLog Error - %d\n", GetLastError());
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+ //
+ // Now just clear the file without backing it up
+ //
+ printf("Calling ClearEventLog with no backup ");
+ Status = ClearEventLogW (
+ LogHandle,
+ NULL
+ );
+
+ if (!Status) {
+ printf ("ClearEventLogError - %d\n", GetLastError());
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+ printf("Calling CloseEventLog\n");
+ IStatus = CloseEventLog (LogHandle);
+ }
+
+ return(Status);
+}
+
+BOOL
+TestBackupLogFile (LPSTR BackupFileName)
+
+{
+ BOOL Status, IStatus;
+ HANDLE LogHandle;
+ LPWSTR ModuleName;
+ ANSI_STRING AnsiString;
+ UNICODE_STRING UnicodeString;
+
+ printf("Testing BackupLogFile API\n");
+ //
+ // Initialize the strings
+ //
+ ModuleName = L"TESTWINAPP";
+
+ //
+ // Open the log handle
+ //
+ printf("Calling OpenEventLog for BACKUP - ");
+ LogHandle = OpenEventLogW (
+ NULL,
+ ModuleName
+ );
+
+ if (LogHandle == NULL) {
+ printf("OpenEventLog Failure %d\n", GetLastError());
+
+ } else {
+ printf("OpenEventLog SUCCESS\n");
+
+ //
+ // Backup the log file to BackupFileName
+ //
+
+ printf("Calling BackupEventLog backing up to %s ", BackupFileName);
+
+ RtlInitAnsiString(&AnsiString, BackupFileName);
+ RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE);
+
+ Status = BackupEventLogW (LogHandle, UnicodeString.Buffer);
+
+ if (!Status) {
+ printf ("BackupEventLog failure - %d\n", GetLastError());
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+ printf("Calling CloseEventLog\n");
+ IStatus = CloseEventLog (LogHandle);
+ }
+
+ return(Status);
+}
+
+VOID
+NotifyThread(
+ HANDLE hEventLog)
+{
+ Sleep(30000);
+ printf("NotifyThread: Writing an event...\n");
+ if (!WriteLogEntryMsg(hEventLog,1)) {
+ printf("NotifyThread: WriteLogEntryMsg failed\n");
+ }
+ else {
+ printf("Event was written\n");
+ }
+ ExitThread(NO_ERROR);
+}
+
+VOID
+TestChangeNotify(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ HANDLE hEvent;
+ HANDLE hThread;
+ HANDLE hEventLog;
+ DWORD threadId;
+ DWORD status;
+
+ hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
+ if (hEvent == NULL) {
+ printf("CreateEvent Failed %d\n",GetLastError());
+ return;
+ }
+#ifdef TEST_REMOTE
+ hEventLog = RegisterEventSourceW(L"\\\\DANL2",L"TESTWINAPP");
+#else
+ hEventLog = RegisterEventSourceW(NULL,L"TESTWINAPP");
+#endif
+ if (hEventLog == NULL) {
+ printf("OpenEventLog failed %d\n",GetLastError());
+ }
+
+#ifdef TEST_NOTIFY
+
+ if (!NotifyChangeEventLog(hEventLog,hEvent)) {
+ printf("NotifyChangeEventLog failed %d\n",GetLastError());
+ }
+#endif // TEST_NOTIFY
+
+ hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)NotifyThread,hEventLog,0,&threadId);
+ if (hThread == NULL) {
+ printf("CreateThread Failed %d\n",GetLastError());
+ CloseHandle(hEvent);
+ return;
+ }
+
+ CloseHandle(hThread);
+
+ printf("Wait for event to become signaled\n");
+ status = WaitForSingleObject(hEvent,INFINITE);
+ if (status == WAIT_OBJECT_0) {
+ printf("The Event was signaled\n");
+ }
+ else {
+ printf("The Event was NOT signaled\n");
+ }
+ return;
+}
+/****************************************************************************/
+DWORD _CRTAPI1
+main (
+ IN SHORT argc,
+ IN PSZ argv[],
+ IN PSZ envp[]
+ )
+/*++
+*
+* Routine Description:
+*
+*
+*
+* Arguments:
+*
+*
+*
+*
+* Return Value:
+*
+*
+*
+--*/
+/****************************************************************************/
+{
+
+ DWORD ReadFlags;
+
+ Initialize(); // Init any data
+
+ if ( argc < 2 ) {
+ printf( "Not enough parameters\n" );
+ return Usage( );
+ }
+
+ if ( stricmp( argv[1], "-c" ) == 0 ) {
+
+ if ( argc < 3 ) {
+ return TestClearLogFile();
+ }
+ }
+ else if ( stricmp( argv[1], "-b" ) == 0 ) {
+
+ if ( argc < 3 ) {
+ printf("You must supply a filename to backup to\n");
+ return(FALSE);
+ }
+
+ return TestBackupLogFile(argv[2]);
+
+ } else if (stricmp ( argv[1], "-rsf" ) == 0 ) {
+
+ ReadFlags = EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ;
+ if ( argc < 3 ) {
+ return TestReadEventLog(1,ReadFlags,0 );
+ } else {
+ return Usage();
+ }
+ } else if (stricmp ( argv[1], "-rsb" ) == 0 ) {
+
+ ReadFlags = EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ;
+ if ( argc < 3 ) {
+ return TestReadEventLog(1,ReadFlags,0 );
+ } else {
+ return Usage();
+ }
+ } else if (stricmp ( argv[1], "-n" ) == 0 ) {
+ TestChangeNotify();
+
+ } else if (stricmp ( argv[1], "-rrf" ) == 0 ) {
+
+ ReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ;
+ if ( argc < 3 ) {
+ return TestReadEventLog(1,ReadFlags ,1);
+ } else if (argc == 3) {
+ return (TestReadEventLog (1, ReadFlags, atoi(argv[2])));
+ }
+ } else if (stricmp ( argv[1], "-rrb" ) == 0 ) {
+
+ ReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_BACKWARDS_READ;
+ if ( argc < 3 ) {
+ return TestReadEventLog(1,ReadFlags, 1);
+ } else if (argc == 3) {
+ return (TestReadEventLog (1, ReadFlags, atoi(argv[2])));
+ }
+ } else if (stricmp ( argv[1], "-w" ) == 0 ) {
+
+ if ( argc < 3 ) {
+ return TestWriteEventLog(1);
+ } else if (argc == 3) {
+ return (TestWriteEventLog (atoi(argv[2])));
+ }
+
+ } else {
+
+ return Usage();
+ }
+
+ UNREFERENCED_PARAMETER(argc);
+ UNREFERENCED_PARAMETER(argv);
+ UNREFERENCED_PARAMETER(envp);
+
+
+}
diff --git a/private/eventlog/test/testwina.c b/private/eventlog/test/testwina.c
new file mode 100644
index 000000000..efd558f57
--- /dev/null
+++ b/private/eventlog/test/testwina.c
@@ -0,0 +1,693 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ TESTWINA.C
+
+Abstract:
+
+ Test program for the eventlog service. This program calls the Win
+ APIs to test out the operation of the service.
+
+Author:
+
+ Rajen Shah (rajens) 05-Aug-1991
+
+Revision History:
+
+
+--*/
+/*----------------------*/
+/* INCLUDES */
+/*----------------------*/
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <stdio.h> // printf
+#include <string.h> // stricmp
+#include <stdlib.h>
+#include <windows.h>
+#include <netevent.h>
+//#include <elfcommn.h>
+
+
+#define READ_BUFFER_SIZE 1024*2 // Use 2K buffer
+
+#define SIZE_DATA_ARRAY 65
+
+//
+// Global buffer used to emulate "binary data" when writing an event
+// record.
+//
+DWORD Data[SIZE_DATA_ARRAY];
+BOOL bHackTestBackup = FALSE;
+PCHAR pServerName = NULL;
+
+
+VOID
+Initialize (
+ VOID
+ )
+{
+ DWORD i;
+
+ // Initialize the values in the data buffer.
+ //
+ for (i=0; i< SIZE_DATA_ARRAY; i++)
+ Data[i] = i;
+
+}
+
+
+BOOL
+Usage (
+ VOID
+ )
+{
+ printf( "usage: \n" );
+ printf( "-c Tests ClearEventLog API\n");
+ printf( "-b filename Tests BackupEventLog API\n");
+ printf( "-rsb Reads event log sequentially backwards\n");
+ printf( "-rsf Reads event log sequentially forwards\n");
+ printf( "-rrb <record> Reads event log from <record> backwards\n");
+ printf( "-rrf <record> Reads event log from <record> forwards\n");
+ printf( "-w <count> Tests ReportEvent API <count> times\n");
+ return ERROR_INVALID_PARAMETER;
+
+} // Usage
+
+
+
+BOOL
+WriteLogEntry ( HANDLE LogHandle, DWORD EventID )
+
+{
+#define NUM_STRINGS 2
+#define MAX_STRING_SIZE 32767 // Max size is FFFF/2 for ANSI strings
+
+ BOOL Status;
+ WORD EventType;
+ DWORD i;
+ DWORD DataSize;
+ PSID pUserSid;
+ PCHAR BigString;
+
+ // PSTR Strings[NUM_STRINGS] = {"StringAOne","StringATwo" };
+ PSTR Strings[NUM_STRINGS];
+
+ Strings[0] = "StringAOne";
+
+ BigString = malloc(MAX_STRING_SIZE);
+
+ for (i = 0; i < MAX_STRING_SIZE; i++) {
+ BigString[i] = 'A';
+ }
+
+ BigString[MAX_STRING_SIZE-1] = '\0';
+ Strings[1] = BigString;
+
+ EventType = EVENTLOG_INFORMATION_TYPE;
+ pUserSid = NULL;
+ DataSize = sizeof(DWORD) * SIZE_DATA_ARRAY;
+
+ for (i=0; i< SIZE_DATA_ARRAY; i++)
+ Data[i] += i;
+
+ Status = ReportEventA (
+ LogHandle,
+ EventType,
+ 0, // event category
+ EventID,
+ pUserSid,
+ (WORD) NUM_STRINGS,
+ DataSize,
+ Strings,
+ (PVOID)Data
+ );
+
+ free(BigString);
+ return (Status);
+}
+
+BOOL
+WriteLogEntryMsg ( HANDLE LogHandle, DWORD EventID )
+/*
+ This function requires a registry entry in the Applications section
+ of the Eventlog for TESTWINAAPP, it will use the netevent.dll message file.
+*/
+{
+#define NUM_STRINGS 2
+
+ BOOL Status;
+ WORD EventType;
+ DWORD DataSize;
+ PSID pUserSid;
+ PCHAR BigString;
+
+ PSTR Strings[NUM_STRINGS];
+
+ Strings[0] = "This is a BOGUS message for TEST purposes Ignore this substitution text";
+ Strings[1] = "GHOST SERVICE in the long string format - I wanted a long string to pass into this function";
+
+ EventType = EVENTLOG_INFORMATION_TYPE;
+ pUserSid = NULL;
+ DataSize = sizeof(DWORD) * SIZE_DATA_ARRAY;
+
+ Status = ReportEventA (
+ LogHandle,
+ EventType,
+ 0, // event category
+ EVENT_SERVICE_START_FAILED_NONE,
+ pUserSid,
+ (WORD) NUM_STRINGS,
+ 0, // DataSize
+ Strings,
+ (PVOID)NULL // Data
+ );
+
+ free(BigString);
+ return (Status);
+}
+
+
+VOID
+DisplayEventRecords( PVOID Buffer,
+ DWORD BufSize,
+ ULONG *NumRecords)
+
+{
+ PEVENTLOGRECORD pLogRecord;
+ PSTR pString;
+ DWORD Count = 0;
+ DWORD Offset = 0;
+ DWORD i;
+
+ pLogRecord = (PEVENTLOGRECORD) Buffer;
+
+ while ((DWORD)Offset < BufSize) {
+
+ Count++;
+
+ printf("\n\nRecord # %lu\n", pLogRecord->RecordNumber);
+
+ printf("Length: 0x%lx TimeGenerated: 0x%lx EventID: 0x%lx EventType: 0x%x\n",
+ pLogRecord->Length, pLogRecord->TimeGenerated, pLogRecord->EventID,
+ pLogRecord->EventType);
+
+ printf("NumStrings: 0x%x StringOffset: 0x%lx UserSidLength: 0x%lx TimeWritten: 0x%lx\n",
+ pLogRecord->NumStrings, pLogRecord->StringOffset,
+ pLogRecord->UserSidLength, pLogRecord->TimeWritten);
+
+ printf("UserSidOffset: 0x%lx DataLength: 0x%lx DataOffset: 0x%lx \n",
+ pLogRecord->UserSidOffset, pLogRecord->DataLength,
+ pLogRecord->DataOffset);
+
+ //
+ // Print out module name
+ //
+ pString = (PSTR)((DWORD)pLogRecord + sizeof(EVENTLOGRECORD));
+ printf("ModuleName: %s ", pString);
+
+ //
+ // Display ComputerName
+ //
+ pString = (PSTR)((DWORD)pString + strlen(pString) + 1);
+ printf("ComputerName: %s\n",pString);
+
+ //
+ // Display strings
+ //
+ pString = (PSTR)((DWORD)Buffer + pLogRecord->StringOffset);
+
+ printf("Strings: ");
+ for (i=0; i<pLogRecord->NumStrings; i++) {
+
+ printf(" %s ", pString);
+ pString = (PSTR)((DWORD)pString + strlen(pString) + 1);
+ }
+
+ // Get next record
+
+ Offset += pLogRecord->Length;
+
+ pLogRecord = (PEVENTLOGRECORD)((DWORD)Buffer + Offset);
+
+ }
+ *NumRecords = Count;
+
+}
+
+
+BOOL
+ReadFromLog ( HANDLE LogHandle,
+ PVOID Buffer,
+ ULONG *pBytesRead,
+ DWORD ReadFlag,
+ DWORD Record
+ )
+{
+ BOOL Status;
+ DWORD MinBytesNeeded;
+ DWORD ErrorCode;
+
+ Status = ReadEventLogA (
+ LogHandle,
+ ReadFlag,
+ Record,
+ Buffer,
+ READ_BUFFER_SIZE,
+ pBytesRead,
+ &MinBytesNeeded
+ );
+
+
+ if (!Status) {
+ ErrorCode = GetLastError();
+ printf("Error from ReadEventLog %d \n", ErrorCode);
+ if (ErrorCode == ERROR_NO_MORE_FILES)
+ printf("Buffer too small. Need %lu bytes min\n", MinBytesNeeded);
+
+ }
+
+ return (Status);
+}
+
+
+
+
+BOOL
+TestReadEventLog (DWORD Count, DWORD ReadFlag, DWORD Record)
+
+{
+ BOOL Status, IStatus;
+
+ HANDLE LogHandle;
+ LPSTR ModuleName;
+ DWORD NumRecords, BytesReturned;
+ PVOID Buffer;
+ DWORD RecordOffset;
+ DWORD NumberOfRecords;
+ DWORD OldestRecord;
+
+ printf("Testing ReadEventLog API to read %lu entries\n",Count);
+
+ Buffer = malloc (READ_BUFFER_SIZE);
+
+ //
+ // Initialize the strings
+ //
+ NumRecords = Count;
+ ModuleName = "TESTWINAAPP";
+
+ //
+ // Open the log handle
+ //
+
+ //
+ // This is just a quick and dirty way to test the api to read a backup
+ // log, until I can fix test.c to be more general purpose.
+ //
+
+ if (bHackTestBackup) {
+ printf("OpenBackupEventLog = ");
+ LogHandle = OpenBackupEventLog(
+ NULL,
+ "\\\\danhi386\\roote\\view.log"
+ );
+ }
+ else {
+ printf("OpenEventLog - ");
+ LogHandle = OpenEventLog (
+ pServerName,
+ ModuleName
+ );
+ }
+
+ if (LogHandle == NULL) {
+ printf("Error - %d\n", GetLastError());
+
+ } else {
+ printf("SUCCESS\n");
+
+ //
+ // Get and print record information
+ //
+
+ Status = GetNumberOfEventLogRecords(LogHandle, & NumberOfRecords);
+ if (NT_SUCCESS(Status)) {
+ Status = GetOldestEventLogRecord(LogHandle, & OldestRecord);
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ printf("Get of record information failed with %X", Status);
+ return(Status);
+ }
+
+ printf("\nThere are %d records in the file, %d is the oldest"
+ " record number\n", NumberOfRecords, OldestRecord);
+
+ RecordOffset = Record;
+
+ while (Count && (BytesReturned != 0)) {
+
+ printf("Read %u records\n", NumRecords);
+ //
+ // Read from the log
+ //
+ Status = ReadFromLog ( LogHandle,
+ Buffer,
+ &BytesReturned,
+ ReadFlag,
+ RecordOffset
+ );
+ if (Status) {
+ printf("Bytes read = 0x%lx\n", BytesReturned);
+ DisplayEventRecords(Buffer, BytesReturned, &NumRecords);
+ Count -= NumRecords;
+ RecordOffset += NumRecords;
+ } else {
+ break;
+ }
+
+ }
+ printf("\n");
+
+ if (!Status) {
+ printf ("Error - %d. Remaining count %lu\n", GetLastError(), Count);
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+ printf("Calling CloseEventLog\n");
+ IStatus = CloseEventLog (LogHandle);
+ }
+ free(Buffer);
+ return (Status);
+}
+
+
+
+BOOL
+TestWriteEventLog (DWORD Count)
+
+{
+ BOOL Status, IStatus;
+ HANDLE LogHandle=NULL;
+ LPSTR ModuleName;
+ DWORD EventID = 99;
+ DWORD WriteCount;
+
+ printf("Testing ReportEvent API\n");
+
+ //
+ // Initialize the strings
+ //
+ ModuleName = "TESTWINAAPP";
+
+ //
+ // Open the log handle
+ //
+ while (Count && NT_SUCCESS(Status)) {
+ //printf("Calling RegisterEventSource for WRITE %lu times - ", Count);
+ LogHandle = RegisterEventSourceA (
+ pServerName,
+ ModuleName
+ );
+
+ if (LogHandle == NULL) {
+ printf("Error - %d\n", GetLastError());
+
+ } else {
+ printf("Registered - ");
+ WriteCount = 5;
+ printf("Record # %u ", Count);
+
+ while (WriteCount && NT_SUCCESS(Status)) {
+
+ //
+ // Write an entry into the log
+ //
+ Data[0] = Count; // Make data "unique"
+ EventID = (EventID + Count) % 100; // Vary the eventids
+ Status = WriteLogEntryMsg ( LogHandle, EventID );
+ Count--;
+ WriteCount--;
+
+ if (!Status) {
+ printf ("Error - %d. Remaining count %lu\n", GetLastError(), Count);
+ } else {
+ printf ("%d,",WriteCount);
+ }
+ }
+ IStatus = DeregisterEventSource (LogHandle);
+ printf(" - Deregistered\n");
+ }
+ }
+
+ return (Status);
+}
+
+
+
+BOOL
+TestClearLogFile ()
+
+{
+ BOOL Status, IStatus;
+ HANDLE LogHandle;
+ LPSTR ModuleName, BackupName;
+
+ printf("Testing ClearLogFile API\n");
+ //
+ // Initialize the strings
+ //
+ ModuleName = "TESTWINAAPP";
+
+ //
+ // Open the log handle
+ //
+ printf("Calling OpenEventLog for CLEAR - ");
+ LogHandle = OpenEventLogA (
+ pServerName,
+ ModuleName
+ );
+
+ if (!Status) {
+ printf("Error - %d\n", GetLastError());
+
+ } else {
+ printf("SUCCESS\n");
+
+ //
+ // Clear the log file and back it up to "view.log"
+ //
+
+ printf("Calling ClearEventLog backing up to view.log ");
+ BackupName = "view.log";
+
+ Status = ClearEventLogA (
+ LogHandle,
+ BackupName
+ );
+
+ if (!Status) {
+ printf ("Error - %d\n", GetLastError());
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+ //
+ // Now just clear the file without backing it up
+ //
+ printf("Calling ClearEventLog with no backup ");
+ Status = ClearEventLogA (
+ LogHandle,
+ NULL
+ );
+
+ if (!Status) {
+ printf ("Error - %d\n", GetLastError());
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+ printf("Calling CloseEventLog\n");
+ IStatus = CloseEventLog (LogHandle);
+ }
+
+ return(Status);
+}
+
+
+BOOL
+TestBackupLogFile(
+ LPSTR FileName
+ )
+
+{
+ HANDLE LogHandle;
+
+ printf("Testing BackupEventLog API\n");
+
+ //
+ // Open the log handle
+ //
+
+ printf("Calling ElfOpenEventLog for BACKUP - ");
+ LogHandle = OpenEventLogA (
+ NULL,
+ "Application"
+ );
+
+ if (!LogHandle) {
+ printf("Error - %d\n", GetLastError());
+
+ } else {
+ printf("SUCCESS\n");
+
+ //
+ // Backup the log file
+ //
+
+ printf("Calling BackupEventLogFile backing up to %s\n", FileName);
+
+ if (!BackupEventLogA (
+ LogHandle,
+ FileName
+ )) {
+ printf ("Error - %d\n", GetLastError());
+ } else {
+ printf ("SUCCESS\n");
+ }
+
+
+ printf("Calling CloseEventLog - ");
+ if (CloseEventLog (LogHandle)) {
+ printf("Success\n");
+ }
+ else {
+ printf("Failed with code %d\n", GetLastError());
+ }
+ }
+
+ return(TRUE);
+}
+
+
+/****************************************************************************/
+BOOL
+main (
+ IN SHORT argc,
+ IN PSZ argv[],
+ )
+/*++
+*
+* Routine Description:
+*
+*
+*
+* Arguments:
+*
+*
+*
+*
+* Return Value:
+*
+*
+*
+--*/
+/****************************************************************************/
+{
+
+ DWORD ReadFlags;
+
+ Initialize(); // Init any data
+
+ //
+ // Just till I can replace this horrid parm parsing with my own
+ //
+
+ if (getenv("REMOTE")) {
+ pServerName = "\\\\danhi20";
+ }
+
+ if ( argc < 2 ) {
+ printf( "Not enough parameters\n" );
+ return Usage( );
+ }
+
+ if ( stricmp( argv[1], "-c" ) == 0 ) {
+
+ if ( argc < 3 ) {
+ return TestClearLogFile();
+ }
+
+ } else if (stricmp ( argv[1], "-b" ) == 0 ) {
+
+ if ( argc < 3 ) {
+ return Usage();
+ } else {
+ return TestBackupLogFile(argv[2]);
+ }
+
+ } else if (stricmp ( argv[1], "-rsf" ) == 0 ) {
+
+ ReadFlags = EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ;
+ if ( argc < 3 ) {
+ return TestReadEventLog(1,ReadFlags,0 );
+ } else {
+ return Usage();
+ }
+ } else if (stricmp ( argv[1], "-xsf" ) == 0 ) {
+
+ ReadFlags = EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ;
+ bHackTestBackup = TRUE;
+ if ( argc < 3 ) {
+ return TestReadEventLog(1,ReadFlags,0 );
+ } else {
+ return Usage();
+ }
+ } else if (stricmp ( argv[1], "-rsb" ) == 0 ) {
+
+ ReadFlags = EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ;
+ if ( argc < 3 ) {
+ return TestReadEventLog(1,ReadFlags,0 );
+ } else {
+ return Usage();
+ }
+ } else if (stricmp ( argv[1], "-rrf" ) == 0 ) {
+
+ ReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ;
+ if ( argc < 3 ) {
+ return TestReadEventLog(1,ReadFlags ,1);
+ } else if (argc == 3) {
+ return (TestReadEventLog (1, ReadFlags, atoi(argv[2])));
+ }
+ } else if (stricmp ( argv[1], "-rrb" ) == 0 ) {
+
+ ReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_BACKWARDS_READ;
+ if ( argc < 3 ) {
+ return TestReadEventLog(1,ReadFlags, 1);
+ } else if (argc == 3) {
+ return (TestReadEventLog (1, ReadFlags, atoi(argv[2])));
+ }
+ } else if (stricmp ( argv[1], "-w" ) == 0 ) {
+
+ if ( argc < 3 ) {
+ return TestWriteEventLog(1);
+ } else if (argc == 3) {
+ return (TestWriteEventLog (atoi(argv[2])));
+ }
+
+ } else {
+
+ return Usage();
+ }
+
+ UNREFERENCED_PARAMETER(argc);
+ UNREFERENCED_PARAMETER(argv);
+
+
+}