1488 lines
38 KiB
C
1488 lines
38 KiB
C
/*++
|
||
|
||
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,
|
||
TestFull,
|
||
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");
|
||
printf( "-z Test to see if the logs are full\n");
|
||
exit(0);
|
||
|
||
} // Usage
|
||
|
||
|
||
NTSTATUS
|
||
WriteLogEntry (
|
||
HANDLE LogHandle,
|
||
ULONG EventID
|
||
)
|
||
{
|
||
#define NUM_STRINGS 2
|
||
#define SIZE_TOKEN_BUFFER 512
|
||
|
||
SYSTEMTIME systime;
|
||
NTSTATUS Status;
|
||
USHORT EventType, i;
|
||
ULONG DataSize;
|
||
PSID pUserSid = NULL;
|
||
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]);
|
||
UStrings[i]->MaximumLength = UStrings[i]->Length + sizeof(WCHAR);
|
||
}
|
||
|
||
//
|
||
// Vary the data sizes.
|
||
//
|
||
|
||
GetLocalTime(&systime);
|
||
|
||
DataSize = systime.wMilliseconds % sizeof(Data);
|
||
printf("\nData Size = %lu\n", DataSize);
|
||
|
||
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)
|
||
//
|
||
|
||
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);
|
||
pLogRecord = (PEVENTLOGRECORD)((BYTE *)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 + sizeof(WCHAR);
|
||
|
||
//
|
||
// 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 + sizeof(WCHAR);
|
||
|
||
//
|
||
// 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 + sizeof(WCHAR);
|
||
|
||
//
|
||
// 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 + sizeof(WCHAR);
|
||
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 + sizeof(WCHAR);
|
||
|
||
//
|
||
// 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 + sizeof(WCHAR);
|
||
|
||
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 + sizeof(WCHAR);
|
||
|
||
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
|
||
TestLogFull(
|
||
VOID
|
||
)
|
||
{
|
||
HANDLE hLogFile;
|
||
BOOL fIsFull;
|
||
BOOLEAN fPrevious = FALSE;
|
||
DWORD i;
|
||
DWORD dwBytesNeeded;
|
||
BOOL fIsSecLog;
|
||
|
||
LPWSTR szLogNames[] = { L"Application", L"Security", L"System" };
|
||
|
||
for (i = 0; i < sizeof(szLogNames) / sizeof(LPWSTR); i++) {
|
||
|
||
fIsSecLog = (wcscmp(szLogNames[i], L"Security") == 0);
|
||
|
||
if (fIsSecLog) {
|
||
|
||
if (!NT_SUCCESS(RtlAdjustPrivilege(SE_SECURITY_PRIVILEGE,
|
||
TRUE,
|
||
FALSE,
|
||
&fPrevious))) {
|
||
|
||
printf("RtlAdjustPrivilege FAILED %d\n",
|
||
RtlNtStatusToDosError(GetLastError()));
|
||
}
|
||
}
|
||
|
||
hLogFile = OpenEventLogW(NULL, szLogNames[i]);
|
||
|
||
if (hLogFile != NULL) {
|
||
|
||
if (GetEventLogInformation(hLogFile,
|
||
0, // Log full infolevel
|
||
(LPBYTE)&fIsFull,
|
||
sizeof(fIsFull),
|
||
&dwBytesNeeded)) {
|
||
|
||
printf("The %ws Log is%sfull\n",
|
||
szLogNames[i],
|
||
fIsFull ? " " : " not ");
|
||
}
|
||
else {
|
||
|
||
printf("GetEventLogInformation FAILED %d for the %ws Log\n",
|
||
GetLastError(),
|
||
szLogNames[i]);
|
||
}
|
||
}
|
||
else {
|
||
|
||
printf("OpenEventLog FAILED %d for the %ws Log\n",
|
||
GetLastError(),
|
||
szLogNames[i]);
|
||
}
|
||
|
||
if (fIsSecLog) {
|
||
RtlAdjustPrivilege(SE_SECURITY_PRIVILEGE, fPrevious, FALSE, &fPrevious);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
__cdecl
|
||
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;
|
||
|
||
case TestFull:
|
||
TestLogFull();
|
||
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;
|
||
|
||
case 'z':
|
||
|
||
SET_OPERATION(TestFull)
|
||
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");
|
||
}
|
||
}
|
||
|