windows-nt/Source/XPSP1/NT/admin/snapin/smonlog/smlogsvc/smlogsvc.c
2020-09-26 16:20:57 +08:00

4948 lines
156 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1996-1999 Microsoft Corporation
Module Name:
smlogsvc.c
Abstract:
service to log performance counter and trace data,
and to scan for alert conditions.
--*/
#ifndef UNICODE
#define UNICODE 1
#endif
#ifndef _UNICODE
#define _UNICODE 1
#endif
#ifndef _IMPLEMENT_WMI
#define _IMPLEMENT_WMI 1
#endif
#ifndef _DEBUG_OUTPUT
#define _DEBUG_OUTPUT 0
#endif
//
// Windows Include files
//
#pragma warning ( disable : 4201)
#pragma warning ( disable : 4127)
// Define the following to use the minimum of shlwapip.h
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <wtypes.h>
#include <limits.h>
#if _IMPLEMENT_WMI
#include <wmistr.h>
#include <objbase.h>
#include <initguid.h>
#include <evntrace.h>
#include <wmiguid.h>
#include <wmium.h>
#include <pdhmsg.h> // For BuildCurrentLogFileName
#include <pdhp.h>
#endif
#include <tchar.h>
#include <assert.h>
#include <limits.h>
#include "common.h"
#include "smlogsvc.h"
#include "smlogmsg.h"
#define NT_KERNEL_LOGGER ((LPWSTR)L"NT Kernel Logger")
#define DEFAULT_LOG_FILE_FOLDER L"%SystemDrive%\\PerfLogs"
#define STATUS_MASK ((DWORD)0x3FFFFFFF)
// todo: Move SECONDS_IN_DAY definition
#define SECONDS_IN_DAY ((LONGLONG)(86400))
// Global variables used by all modules
HANDLE hEventLog = NULL;
HINSTANCE hModule = NULL;
DWORD* arrPdhDataCollectSuccess = NULL;
INT iPdhDataCollectSuccessCount = 0;
// hNewQueryEvent is signalled when a new query is started. This tells the main
// thread to reconfigure its array of Wait objects.
HANDLE hNewQueryEvent = NULL;
SERVICE_STATUS_HANDLE hSmLogStatus;
SERVICE_STATUS ssSmLogStatus;
// Static variables used by this module only
static PLOG_QUERY_DATA pFirstQuery = NULL;
static CRITICAL_SECTION QueryDataLock;
static CRITICAL_SECTION ConfigurationLock;
static TCHAR gszDefaultLogFileFolder[MAX_PATH+1] = TEXT("");
// Active session count should match the number of query data objects.
static DWORD dwActiveSessionCount = 0;
static DWORD dwMaxActiveSessionCount = MAXIMUM_WAIT_OBJECTS - 1;
static HANDLE arrSessionHandle[MAXIMUM_WAIT_OBJECTS];
// Local function prototypes
DWORD
LoadCommonConfig(
IN PLOG_QUERY_DATA pQuery);
void
LockQueryData ( void );
void
UnlockQueryData ( void );
PLOG_QUERY_DATA
GetQueryData (
LPCTSTR szQueryName );
void
FreeQueryData (
IN PLOG_QUERY_DATA pQuery );
void
RemoveAndFreeQueryData (
HANDLE hThisQuery );
BOOL
AlertFieldsMatch (
IN PLOG_QUERY_DATA pFirstQuery,
IN PLOG_QUERY_DATA pSecondQuery );
BOOL
CommonFieldsMatch (
IN PLOG_QUERY_DATA pFirstQuery,
IN PLOG_QUERY_DATA pSecondQuery );
BOOL
FieldsMatch (
IN PLOG_QUERY_DATA pFirstQuery,
IN PLOG_QUERY_DATA pSecondQuery );
DWORD
ConfigureQuery (
HKEY hKeyLogQuery,
TCHAR* szQueryKeyNameBuffer,
TCHAR* szQueryNameBuffer );
void
ClearTraceProperties (
IN PLOG_QUERY_DATA pQuery );
BOOL
TraceStopRestartFieldsMatch (
IN PLOG_QUERY_DATA pOrigQuery,
IN PLOG_QUERY_DATA pNewQuery );
DWORD
ReconfigureQuery (
IN PLOG_QUERY_DATA pQuery );
DWORD
StartQuery (
IN PLOG_QUERY_DATA pQuery );
DWORD
HandleMaxQueriesExceeded (
IN PLOG_QUERY_DATA pQuery );
DWORD
InitTraceGuids(
IN PLOG_QUERY_DATA pQuery );
BOOL
IsKernelTraceMode (
IN DWORD dwTraceFlags );
DWORD
LoadPdhLogUpdateSuccess ( void );
void
LoadDefaultLogFileFolder ( void );
DWORD
ProcessLogFileFolder (
IN PLOG_QUERY_DATA pQuery,
IN BOOL bReconfigure );
#if _IMPLEMENT_WMI
DWORD
IsCreateNewFile (
IN PLOG_QUERY_DATA pQuery,
OUT BOOL* pbValidBySize,
OUT BOOL* pbValidByTime );
ULONG
TraceNotificationCallback(
IN PWNODE_HEADER pWnode,
IN UINT_PTR LogFileIndex )
{
UNREFERENCED_PARAMETER(LogFileIndex);
if ( (IsEqualGUID(& pWnode->Guid, & TraceErrorGuid))
&& (pWnode->BufferSize >= (sizeof(WNODE_HEADER) + sizeof(ULONG))))
{
ULONG LoggerId = (ULONG) pWnode->HistoricalContext;
PLOG_QUERY_DATA pQuery = pFirstQuery;
ULONG Status = * ((ULONG *)
(((PUCHAR) pWnode) + sizeof(WNODE_HEADER)));
LOG_QUERY_DATA lqdTemp;
HRESULT hr = ERROR_SUCCESS;
DWORD dwStatus = ERROR_SUCCESS;
while ( NULL != pQuery ) {
// todo: Need to lock queue?
if (pQuery->Properties.Wnode.HistoricalContext == LoggerId) {
break;
}
pQuery = pQuery->next;
}
if ( STATUS_LOG_FILE_FULL == Status
|| STATUS_THREAD_IS_TERMINATING == Status ) {
if ( NULL != pQuery ) {
SetEvent (pQuery->hExitEvent);
}
} else if ( STATUS_MEDIA_CHANGED == Status ) {
BOOL bRun = TRUE;
if ( NULL != pQuery ) {
if( pQuery->hUserToken == NULL ){
// see if we can get a user token
hr = PdhiPlaRunAs( pQuery->szQueryName, NULL, &pQuery->hUserToken );
if ( ERROR_SUCCESS != hr ){
LPWSTR szStringArray[2];
szStringArray[0] = pQuery->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_INVALID_CREDENTIALS,
NULL,
1,
sizeof(HRESULT),
szStringArray,
(LPVOID)&hr
);
bRun = FALSE;
}
}
// Run command file, supplying previous filename
if ( bRun && NULL != pQuery->szCmdFileName ) {
DoLogCommandFile (pQuery, pQuery->szLogFileName, TRUE);
}
}
// Retrieve the current log file name for the next notification.
dwStatus = GetTraceQueryStatus ( pQuery, &lqdTemp );
if ( ERROR_SUCCESS == dwStatus ) {
lstrcpy ( pQuery->szLogFileName, lqdTemp.szLogFileName );
RegisterCurrentFile( pQuery->hKeyQuery, pQuery->szLogFileName, 0 );
} // else { todo report error
// Query to get the new filename
} else {
// report error
}
}
return ERROR_SUCCESS;
}
#endif
// Functions
DWORD
GetSystemWideDefaultNullDataSource()
{
static BOOLEAN bRead = FALSE;
static DWORD dwNullDataSource = DATA_SOURCE_REGISTRY;
if (bRead == FALSE) {
HKEY hKeyPDH = NULL;
DWORD dwStatus;
DWORD dwType = 0;
DWORD dwSize = sizeof(DWORD);
dwStatus = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\PDH",
0L,
KEY_READ,
& hKeyPDH);
if (dwStatus == ERROR_SUCCESS) {
dwStatus = RegQueryValueExW(hKeyPDH,
L"DefaultNullDataSource",
NULL,
& dwType,
(LPBYTE) & dwNullDataSource,
& dwSize);
if ( dwStatus == ERROR_SUCCESS
&& dwType == REG_DWORD
&& dwNullDataSource == DATA_SOURCE_WBEM) {
dwNullDataSource = DATA_SOURCE_WBEM;
}
else {
dwNullDataSource = DATA_SOURCE_REGISTRY;
}
RegCloseKey(hKeyPDH);
}
bRead = TRUE;
}
return dwNullDataSource;
}
DWORD
ScanHexFormat(
IN const WCHAR* Buffer,
IN ULONG MaximumLength,
IN const WCHAR* Format,
...)
/*++
Routine Description:
Scans a source Buffer and places values from that buffer into the parameters
as specified by Format.
Arguments:
Buffer -
Contains the source buffer which is to be scanned.
MaximumLength -
Contains the maximum length in characters for which Buffer is searched.
This implies that Buffer need not be UNICODE_NULL terminated.
Format -
Contains the format string which defines both the acceptable string format
contained in Buffer, and the variable parameters which follow.
NOTE: This code is from \ntos\rtl\guid.c
Return Value:
Returns the number of parameters filled if the end of the Buffer is reached,
else -1 on an error.
--*/
{
va_list ArgList;
int FormatItems;
va_start(ArgList, Format);
for (FormatItems = 0;;) {
switch (*Format) {
case 0:
return (*Buffer && MaximumLength) ? -1 : FormatItems;
case '%':
Format++;
if (*Format != '%') {
ULONG Number;
int Width;
int Long;
PVOID Pointer;
for (Long = 0, Width = 0;; Format++) {
if ((*Format >= '0') && (*Format <= '9')) {
Width = Width * 10 + *Format - '0';
} else if (*Format == 'l') {
Long++;
} else if ((*Format == 'X') || (*Format == 'x')) {
break;
}
}
Format++;
for (Number = 0; Width--; Buffer++, MaximumLength--) {
if (!MaximumLength)
return (DWORD)(-1);
Number *= 16;
if ((*Buffer >= '0') && (*Buffer <= '9')) {
Number += (*Buffer - '0');
} else if ((*Buffer >= 'a') && (*Buffer <= 'f')) {
Number += (*Buffer - 'a' + 10);
} else if ((*Buffer >= 'A') && (*Buffer <= 'F')) {
Number += (*Buffer - 'A' + 10);
} else {
return (DWORD)(-1);
}
}
Pointer = va_arg(ArgList, PVOID);
if (Long) {
*(PULONG)Pointer = Number;
} else {
*(PUSHORT)Pointer = (USHORT)Number;
}
FormatItems++;
break;
}
/* no break */
default:
if (!MaximumLength || (*Buffer != *Format)) {
return (DWORD)(-1);
}
Buffer++;
MaximumLength--;
Format++;
break;
}
}
}
DWORD
GUIDFromString(
IN PUNICODE_STRING GuidString,
OUT GUID* Guid
)
/*++
Routine Description:
Retrieves a the binary format of a textual GUID presented in the standard
string version of a GUID: "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}".
Arguments:
GuidString -
Place from which to retrieve the textual form of the GUID.
Guid -
Place in which to put the binary form of the GUID.
NOTE: This code is from \ntos\rtl\guid.c
Return Value:
Returns ERROR_SUCCESS if the buffer contained a valid GUID, else
ERROR_INVALID_PARAMETER if the string was invalid.
--*/
{
USHORT Data4[8];
int Count;
WCHAR GuidFormat[] = L"{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}";
for (Count = 0; Count < sizeof(Data4)/sizeof(Data4[0]); Count++) {
Data4[Count] = 0;
}
if (ScanHexFormat(GuidString->Buffer, GuidString->Length / sizeof(WCHAR), GuidFormat, &Guid->Data1, &Guid->Data2, &Guid->Data3, &Data4[0], &Data4[1], &Data4[2], &Data4[3], &Data4[4], &Data4[5], &Data4[6], &Data4[7]) == -1) {
return (DWORD)(ERROR_INVALID_PARAMETER);
}
for (Count = 0; Count < sizeof(Data4)/sizeof(Data4[0]); Count++) {
Guid->Data4[Count] = (UCHAR)Data4[Count];
}
return ERROR_SUCCESS;
}
LPWSTR
FormatEventLogMessage(DWORD dwStatus)
{
LPVOID lpMsgBuf = NULL;
HINSTANCE hPdh = NULL;
DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM;
hPdh = LoadLibrary (_T("PDH.DLL"));
if (NULL != hPdh){
dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
}
FormatMessage(
dwFlags,
hPdh,
dwStatus,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
MAX_PATH,
NULL );
if ( NULL != hPdh ) {
FreeLibrary( hPdh );
}
return lpMsgBuf;
}
BOOL
IsKernelTraceMode (
IN DWORD dwTraceFlags )
{
BOOL bReturn = FALSE;
DWORD dwKernelMask = SLQ_TLI_ENABLE_KERNEL_TRACE
| SLQ_TLI_ENABLE_KERNEL_TRACE
| SLQ_TLI_ENABLE_MEMMAN_TRACE
| SLQ_TLI_ENABLE_FILEIO_TRACE
| SLQ_TLI_ENABLE_PROCESS_TRACE
| SLQ_TLI_ENABLE_THREAD_TRACE
| SLQ_TLI_ENABLE_DISKIO_TRACE
| SLQ_TLI_ENABLE_NETWORK_TCPIP_TRACE;
bReturn = ( dwKernelMask & dwTraceFlags ) ? TRUE : FALSE;
return bReturn;
}
long
JulianDateFromSystemTime(
SYSTEMTIME *pST )
{
static WORD wDaysInRegularMonth[] = {
31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
};
static WORD wDaysInLeapYearMonth[] = {
31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
};
long JDate = 0;
// Check for leap year.
if (pST->wMonth > 1) {
if ( ( pST->wYear % 400 == 0 )
|| ( pST->wYear % 100 != 0
&& pST->wYear % 4 == 0 ) ) {
// this is a leap year
JDate += wDaysInLeapYearMonth[pST->wMonth - 2];
} else {
// this is not a leap year
JDate += wDaysInRegularMonth[pST->wMonth - 2];
}
}
// Add in days for this month.
JDate += pST->wDay;
// Add in year.
JDate += (pST->wYear) * 1000;
return JDate;
}
DWORD
ReadRegistrySlqTime (
HKEY hKey,
LPCWSTR szQueryName, // For error logging
LPCWSTR szValueName,
PSLQ_TIME_INFO pPlqtDefault,
PSLQ_TIME_INFO pPlqtValue
)
//
// reads the time value "szValueName" from under hKey and
// returns it in the Value buffer
//
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwType = 0;
DWORD dwBufferSize = 0;
SLQ_TIME_INFO plqLocal;
assert (pPlqtValue != NULL);
assert (szValueName != NULL);
if (hKey != NULL) {
// then there should be something to read
// find out the size of the required buffer
dwStatus = RegQueryValueExW (
hKey,
szValueName,
NULL,
&dwType,
NULL,
&dwBufferSize);
if (dwStatus == ERROR_SUCCESS) {
if ((dwBufferSize == sizeof(SLQ_TIME_INFO)) && (dwType == REG_BINARY)) {
// then there's something to read
dwType = 0;
memset (&plqLocal, 0, sizeof(SLQ_TIME_INFO));
dwStatus = RegQueryValueExW (
hKey,
szValueName,
NULL,
&dwType,
(LPBYTE)&plqLocal,
&dwBufferSize);
if ( ERROR_SUCCESS == dwStatus ) {
*pPlqtValue = plqLocal;
}
} else {
// nothing to read
dwStatus = ERROR_NO_DATA;
}
} else {
// unable to read buffer
// dwStatus has error
}
} else {
// null key
dwStatus = ERROR_BADKEY;
}
if (dwStatus != ERROR_SUCCESS) {
LPCWSTR szStringArray[2];
szStringArray[0] = szValueName;
szStringArray[1] = szQueryName;
// apply default if it exists
if (pPlqtDefault != NULL) {
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_QUERY_VALUE,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
*pPlqtValue = *pPlqtDefault;
dwStatus = ERROR_SUCCESS;
}
// else no default.
// Leave it to the caller to log event.
}
return dwStatus;
}
DWORD
ReadRegistryDwordValue (
HKEY hKey,
LPCWSTR szQueryName,
LPCWSTR szValueName,
PDWORD pdwDefault,
LPDWORD pdwValue
)
//
// reads the DWORD value "szValueName" from under hKey and
// returns it in the Value buffer
//
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwType = 0;
DWORD dwBufferSize = 0;
DWORD dwRegValue;
assert (pdwValue != NULL);
assert (szValueName != NULL);
if (hKey != NULL) {
// then there should be something to read
// find out the size of the required buffer
dwStatus = RegQueryValueExW (
hKey,
szValueName,
NULL,
&dwType,
NULL,
&dwBufferSize);
if (dwStatus == ERROR_SUCCESS) {
if ( (dwBufferSize == sizeof(DWORD))
&& ( (REG_DWORD == dwType) || ( REG_BINARY == dwType) ) ) {
// then there's something to read
dwType = 0;
dwStatus = RegQueryValueExW (
hKey,
szValueName,
NULL,
&dwType,
(LPBYTE)&dwRegValue,
&dwBufferSize);
if (dwStatus == ERROR_SUCCESS) {
*pdwValue = dwRegValue;
}
} else {
// nothing to read
dwStatus = ERROR_NO_DATA;
}
} else {
// unable to read buffer
// dwStatus has error
}
} else {
// null key
dwStatus = ERROR_BADKEY;
}
if (dwStatus != ERROR_SUCCESS) {
LPCWSTR szStringArray[2];
szStringArray[0] = szValueName;
szStringArray[1] = szQueryName;
if (pdwDefault != NULL) {
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_QUERY_VALUE,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
*pdwValue = *pdwDefault;
dwStatus = ERROR_SUCCESS;
} // else no default.
// Leave it to the caller to log event.
}
return dwStatus;
}
DWORD
ReadRegistryStringValue (
HKEY hKey,
LPCWSTR szQueryName,
LPCWSTR szValueName,
LPCWSTR szDefault,
LPWSTR *pszBuffer,
LPDWORD pdwLength
)
//
// reads the string value "szValueName" from under hKey and
// frees any existing buffer referenced by pszBuffer,
// then allocates a new buffer returning it with the
// string value read from the registry and the size of the
// buffer (in bytes)
//
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwType = 0;
DWORD dwBufferSize = 0;
WCHAR* szNewStringBuffer = NULL;
assert (pdwLength!= NULL);
assert (szValueName != NULL);
*pdwLength = 0;
if (hKey != NULL) {
// then there should be something to read
// find out the size of the required buffer
dwStatus = RegQueryValueExW (
hKey,
szValueName,
NULL,
&dwType,
NULL,
&dwBufferSize);
if (dwStatus == ERROR_SUCCESS) {
// NULL character size is 2 bytes
if (dwBufferSize > 2) {
// then there's something to read
szNewStringBuffer = (WCHAR*) G_ALLOC ( dwBufferSize ); // new UCHAR[dwBufferSize];
if (szNewStringBuffer != NULL) {
dwType = 0;
dwStatus = RegQueryValueExW (
hKey,
szValueName,
NULL,
&dwType,
(LPBYTE)szNewStringBuffer,
&dwBufferSize);
if ( 0 == lstrlenW ( szNewStringBuffer ) ) {
dwStatus = ERROR_NO_DATA;
}
} else {
// Todo: Report event for this case.
dwStatus = ERROR_OUTOFMEMORY;
}
} else {
// nothing to read
dwStatus = ERROR_NO_DATA;
}
} // else unable to read buffer
// dwStatus has error
} else {
// null key
dwStatus = ERROR_BADKEY;
}
if (dwStatus != ERROR_SUCCESS) {
LPCWSTR szStringArray[2];
szStringArray[0] = szValueName;
szStringArray[1] = szQueryName;
if (szNewStringBuffer != NULL) {
G_FREE ( szNewStringBuffer ); //delete (szNewStringBuffer);
szNewStringBuffer = NULL;
dwBufferSize = 0;
}
// apply default
if (szDefault != NULL) {
dwBufferSize = lstrlenW(szDefault) + 1;
if ( 1 < dwBufferSize ) {
dwBufferSize *= sizeof (WCHAR);
szNewStringBuffer = (WCHAR*) G_ALLOC ( dwBufferSize );
if (szNewStringBuffer != NULL) {
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_QUERY_VALUE,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
lstrcpyW (
szNewStringBuffer,
szDefault);
dwStatus = ERROR_SUCCESS;
} else {
dwStatus = ERROR_OUTOFMEMORY;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_QUERY_DEF_VAL,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
}
}
} // else no default so no data returned
// Let the caller log the event if they want to.
// Todo: Report event for OUTOFMEMORY case.
}
if (dwStatus == ERROR_SUCCESS) {
// then delete the old buffer and replace it with
// the new one
if (*pszBuffer != NULL) {
G_FREE (*pszBuffer ); //delete (*pszBuffer );
}
*pszBuffer = szNewStringBuffer;
*pdwLength = dwBufferSize;
} else {
// if error then delete the buffer
if (szNewStringBuffer != NULL) {
G_FREE ( szNewStringBuffer ); //delete (szNewStringBuffer);
*pdwLength = 0;
}
}
return dwStatus;
}
DWORD
ReadRegistryIndirectStringValue (
HKEY hKey,
LPCWSTR szQueryName, // For error logging
LPCWSTR szValueName,
LPCWSTR szDefault,
LPWSTR* pszBuffer,
UINT* puiLength )
{
DWORD dwStatus = ERROR_SUCCESS;
LPCWSTR szStringArray[2];
szStringArray[0] = szValueName;
szStringArray[1] = szQueryName;
dwStatus = SmReadRegistryIndirectStringValue (
hKey,
szValueName,
szDefault,
pszBuffer,
puiLength );
/*
Todo: Report event on failure
if ( NULL != szDefault ) {
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_QUERY_VALUE_NODEF,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
}
*/
return dwStatus;
}
DWORD
WriteRegistryDwordValue (
HKEY hKey,
LPCWSTR szValueName,
LPDWORD pdwValue,
DWORD dwType
)
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwValue = sizeof(DWORD);
assert ((dwType == REG_DWORD) ||
(dwType == REG_BINARY));
dwStatus = RegSetValueEx (
hKey, szValueName, 0L,
dwType,
(CONST BYTE *)pdwValue,
dwValue);
return dwStatus;
}
DWORD
WriteRegistrySlqTime (
HKEY hKey,
LPCWSTR szValueName,
PSLQ_TIME_INFO pSlqTime
)
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwValue = sizeof(SLQ_TIME_INFO);
dwStatus = RegSetValueEx (
hKey, szValueName, 0L,
REG_BINARY,
(CONST BYTE *)pSlqTime,
dwValue);
return dwStatus;
}
DWORD
BuildCurrentLogFileName (
IN LPCTSTR szQueryName,
IN LPCTSTR szBaseFileName,
IN LPCTSTR szDefaultDir,
IN LPCTSTR szSqlLogName,
IN LPTSTR szOutFileBuffer,
IN LPDWORD lpdwSerialNumber,
IN DWORD dwAutoNameFormat,
IN DWORD dwLogFileType,
IN INT iCnfSerial
)
// presumes OutFileBuffer is large enough (i.e. >= MAX_PATH+1)
{
DWORD dwStatus = ERROR_SUCCESS;
PPDH_PLA_INFO pInfo = NULL;
DWORD dwStrBufLen = 0;
DWORD dwInfoSize = 0;
DWORD dwFlags = 0;
dwStatus = PdhPlaGetInfo(
(LPTSTR)szQueryName,
NULL,
&dwInfoSize,
pInfo );
if( ERROR_SUCCESS == dwStatus && 0 != dwInfoSize ){
pInfo = (PPDH_PLA_INFO)G_ALLOC(dwInfoSize);
if( NULL != pInfo && (sizeof(PDH_PLA_INFO) <= dwInfoSize) ){
ZeroMemory( pInfo, dwInfoSize );
pInfo->dwMask = PLA_INFO_FLAG_FORMAT|
PLA_INFO_FLAG_FILENAME|
PLA_INFO_FLAG_AUTOFORMAT|
PLA_INFO_FLAG_TYPE|
PLA_INFO_FLAG_DEFAULTDIR|
PLA_INFO_FLAG_SRLNUMBER|
PLA_INFO_FLAG_SQLNAME|
PLA_INFO_FLAG_STATUS;
dwStatus = PdhPlaGetInfo(
(LPTSTR)szQueryName,
NULL,
&dwInfoSize,
pInfo );
pInfo->dwFileFormat = dwLogFileType;
pInfo->strBaseFileName = (LPTSTR)szBaseFileName;
pInfo->dwAutoNameFormat = dwAutoNameFormat;
// PLA_INFO_FLAG_TYPE is counter log vs trace log vs alert
pInfo->strDefaultDir = (LPTSTR)szDefaultDir;
pInfo->dwLogFileSerialNumber = *lpdwSerialNumber;
pInfo->strSqlName = (LPTSTR)szSqlLogName;
dwFlags = PLA_FILENAME_CREATEONLY;
// iCnfSerial = 0 - No serial suffix for Create New File
// iCnfSerial = -1 - Include format string for trace file serial number.
if ( 0 == iCnfSerial ) {
pInfo->ptCreateNewFile.dwAutoMode = SLQ_AUTO_MODE_NONE;
} else {
dwFlags |= PLA_FILENAME_USE_SUBEXT;
if ( -1 == iCnfSerial ) {
dwFlags |= PLA_FILENAME_GET_SUBFMT;
pInfo->ptCreateNewFile.dwAutoMode = SLQ_AUTO_MODE_SIZE;
} else {
pInfo->ptCreateNewFile.dwAutoMode = SLQ_AUTO_MODE_AFTER;
pInfo->dwReserved1 = iCnfSerial;
}
}
dwStatus = PdhPlaGetLogFileName (
(LPTSTR)szQueryName,
NULL,
pInfo,
dwFlags,
&dwStrBufLen,
NULL );
if ( ERROR_SUCCESS == dwStatus || PDH_INSUFFICIENT_BUFFER == dwStatus ) {
// todo: remove buf length restriction
if ( dwStrBufLen <= MAX_PATH * sizeof(WCHAR) ) {
dwStatus = PdhPlaGetLogFileName (
(LPTSTR)szQueryName,
NULL,
pInfo,
dwFlags,
&dwStrBufLen,
szOutFileBuffer );
}
}
}
}
if ( NULL != pInfo ) {
G_FREE( pInfo );
}
return dwStatus;
}
BOOL
FileExists (
IN LPCTSTR szFileName )
{
DWORD dwStatus = ERROR_SUCCESS;
BOOL bFileExists = FALSE;
HANDLE hFile = NULL;
LONG lErrorMode;
if ( NULL != szFileName ) {
lErrorMode = SetErrorMode ( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX );
hFile = CreateFile(
szFileName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING,
NULL
);
if (INVALID_HANDLE_VALUE == hFile ) {
dwStatus = GetLastError();
}
if ( NULL != hFile
&& INVALID_HANDLE_VALUE != hFile
&& ERROR_SUCCESS == dwStatus )
{
bFileExists = TRUE;
}
CloseHandle(hFile);
SetErrorMode ( lErrorMode );
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
return bFileExists;
}
DWORD
LoadCommonConfig(
IN PLOG_QUERY_DATA pQuery)
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwBufferSize = 0;
UINT uiBufferLen = 0;
SLQ_TIME_INFO stiDefault;
DWORD dwDefault;
DWORD dwTempRestart;
SYSTEMTIME stLocalTime;
FILETIME ftLocalTime;
DWORD dwLocalMask = 0;
DWORD dwLocalAttributes = 0;
// Schedule
dwDefault = SLQ_QUERY_STOPPED;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Current State",
&dwDefault,
&pQuery->dwCurrentState);
if ( ERROR_SUCCESS == dwStatus ) {
// Pass NULL default to avoid warning message.
// A missing value here is normal, converting from Win2000 config.
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"RealTime DataSource",
NULL,
&pQuery->dwRealTimeQuery);
if ( ERROR_NO_DATA == dwStatus
|| ERROR_FILE_NOT_FOUND == dwStatus
|| ( 0 == pQuery->dwRealTimeQuery ) ) {
pQuery->dwRealTimeQuery = GetSystemWideDefaultNullDataSource();
dwStatus = ERROR_SUCCESS;
}
}
if ( ERROR_SUCCESS == dwStatus ) {
GetLocalTime (&stLocalTime);
SystemTimeToFileTime (&stLocalTime, &ftLocalTime);
stiDefault.wDataType = SLQ_TT_DTYPE_DATETIME;
stiDefault.wTimeType = SLQ_TT_TTYPE_START;
stiDefault.dwAutoMode = SLQ_AUTO_MODE_AT;
stiDefault.llDateTime = *(LONGLONG *)&ftLocalTime;
dwStatus = ReadRegistrySlqTime (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Start",
&stiDefault,
&pQuery->stiRegStart);
}
if ( ERROR_SUCCESS == dwStatus ) {
stiDefault.wDataType = SLQ_TT_DTYPE_DATETIME;
stiDefault.wTimeType = SLQ_TT_TTYPE_STOP;
stiDefault.dwAutoMode = SLQ_AUTO_MODE_NONE;
stiDefault.llDateTime = MIN_TIME_VALUE;
dwStatus = ReadRegistrySlqTime (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Stop",
&stiDefault,
&pQuery->stiRegStop);
}
if ( ERROR_SUCCESS == dwStatus ) {
// Apply default value outside of Read method, to avoid
// error message. This value does not exist in Windows 2000
dwStatus = ReadRegistrySlqTime (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Create New File",
NULL,
&pQuery->stiCreateNewFile);
if ( ERROR_NO_DATA == dwStatus || ERROR_FILE_NOT_FOUND == dwStatus ) {
stiDefault.wDataType = SLQ_TT_DTYPE_UNITS;
stiDefault.wTimeType = SLQ_TT_TTYPE_CREATE_NEW_FILE;
stiDefault.dwAutoMode = SLQ_AUTO_MODE_NONE;
stiDefault.dwUnitType = SLQ_TT_UTYPE_SECONDS;
stiDefault.dwValue = 0;
pQuery->stiCreateNewFile = stiDefault;
dwStatus = ERROR_SUCCESS;
}
}
// Restart flag is replaced by the Repeat time structure after Windows 2000.
if ( ERROR_SUCCESS == dwStatus ) {
// If autostop, collect Restart value.
// Apply default value outside of Read method, to avoid
// error message. This value does not exist in Windows 2000
if ( pQuery->stiRegStop.dwAutoMode != SLQ_AUTO_MODE_NONE ) {
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Restart",
NULL,
&dwTempRestart );
if ( ERROR_NO_DATA == dwStatus
|| ERROR_FILE_NOT_FOUND == dwStatus )
{
dwTempRestart = SLQ_AUTO_MODE_NONE;
dwStatus = ERROR_SUCCESS;
}
}
}
if ( ERROR_SUCCESS == dwStatus ) {
// If autostop, collect Repeat value.
// Apply default value outside of Read method, to avoid
// error message. This value does not exist in Windows 2000
if ( pQuery->stiRegStop.dwAutoMode != SLQ_AUTO_MODE_NONE ) {
dwStatus = ReadRegistrySlqTime (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Repeat Schedule",
NULL,
&pQuery->stiRepeat );
if ( ERROR_NO_DATA == dwStatus
|| ERROR_FILE_NOT_FOUND == dwStatus
|| SLQ_AUTO_MODE_NONE == pQuery->stiRepeat.dwAutoMode )
{
// If the repeat value doesn't exist or is set to NONE,
// default to the Restart mode value: NONE or AFTER
stiDefault.wDataType = SLQ_TT_DTYPE_UNITS;
stiDefault.wTimeType = SLQ_TT_TTYPE_REPEAT_SCHEDULE;
stiDefault.dwAutoMode = dwTempRestart;
stiDefault.dwUnitType = SLQ_TT_UTYPE_MINUTES;
stiDefault.dwValue = 0;
pQuery->stiRepeat = stiDefault;
dwStatus = ERROR_SUCCESS;
}
}
}
if ( ERROR_SUCCESS == dwStatus ) {
// Todo: Log error events
if ( NULL == pQuery->szLogFileComment ) {
uiBufferLen = 0;
} else {
uiBufferLen = lstrlen ( pQuery->szLogFileComment ) + 1;
}
ReadRegistryIndirectStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCWSTR)L"Comment",
NULL,
&pQuery->szLogFileComment,
&uiBufferLen );
// Ignore status, default is empty.
}
// Todo: File attributes only for counter and trace logs
// File attributes
if ( ERROR_SUCCESS == dwStatus ) {
dwDefault = (DWORD)-1;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Log File Max Size",
&dwDefault,
&pQuery->dwMaxFileSize);
}
if ( ERROR_SUCCESS == dwStatus ) {
dwDefault = SLF_BIN_FILE;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Log File Type",
&dwDefault,
&pQuery->dwLogFileType);
if (dwStatus == ERROR_SUCCESS) {
pQuery->dwLogFileType = LOWORD(pQuery->dwLogFileType);
// For Whistler Beta 1, append mode stored in high word of
// the log type registry value
pQuery->dwAppendMode =
(pQuery->dwLogFileType & 0xFFFF0000) == SLF_FILE_APPEND;
}
}
if ( ERROR_SUCCESS == dwStatus ) {
// Pass NULL default to avoid warning message.
// A missing value here is normal, converting from Win2000 config.
dwLocalAttributes = 0;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Data Store Attributes",
NULL,
&dwLocalAttributes );
// Extract log file size units
if ( ERROR_NO_DATA == dwStatus
|| ERROR_FILE_NOT_FOUND == dwStatus
|| ( 0 == ( dwLocalAttributes & SLF_DATA_STORE_SIZE_MASK ) ) ) {
// If file size unit value is missing, default to Win2000 values
if ( SLQ_COUNTER_LOG == pQuery->dwLogType ) {
if ( SLF_SQL_LOG != pQuery->dwLogFileType ) {
pQuery->dwLogFileSizeUnit = ONE_KB;
} else {
pQuery->dwLogFileSizeUnit = ONE_RECORD;
}
} else if ( SLQ_TRACE_LOG == pQuery->dwLogType ) {
pQuery->dwLogFileSizeUnit = ONE_MB;
}
} else {
if ( dwLocalAttributes & SLF_DATA_STORE_SIZE_ONE_MB ) {
pQuery->dwLogFileSizeUnit = ONE_MB;
} else if ( dwLocalAttributes & SLF_DATA_STORE_SIZE_ONE_KB ) {
pQuery->dwLogFileSizeUnit = ONE_KB;
} else if ( dwLocalAttributes & SLF_DATA_STORE_SIZE_ONE_RECORD ) {
pQuery->dwLogFileSizeUnit = ONE_RECORD;
}
}
// Extract append flag if not already set by Whistler Beta 1 code
if ( 0 == pQuery->dwAppendMode ) {
if ( ERROR_NO_DATA == dwStatus
|| ERROR_FILE_NOT_FOUND == dwStatus
|| ( 0 == ( dwLocalAttributes & SLF_DATA_STORE_APPEND_MASK ) ) )
{
// If file append mode value is missing, default to Win2000 values
assert ( SLF_SQL_LOG != pQuery->dwLogFileType );
if ( SLF_SQL_LOG != pQuery->dwLogFileType ) {
pQuery->dwAppendMode = 0;
}
} else {
pQuery->dwAppendMode = ( dwLocalAttributes & SLF_DATA_STORE_APPEND );
}
}
dwStatus = ERROR_SUCCESS;
}
if ( ERROR_SUCCESS == dwStatus ) {
dwDefault = SLF_NAME_NNNNNN;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Log File Auto Format",
&dwDefault,
&pQuery->dwAutoNameFormat );
}
if ( ERROR_SUCCESS == dwStatus ) {
WCHAR szDefault[MAX_PATH+1];
// Dependent on AutoNameFormat setting.
if ( SLF_NAME_NONE == pQuery->dwAutoNameFormat ) {
// Default log file name is query name, if no autoformat.
lstrcpyW ( ( LPWSTR)szDefault, pQuery->szQueryName );
} else {
szDefault[0] = _T('\0');
}
if ( NULL == pQuery->szBaseFileName ) {
uiBufferLen = 0;
} else {
uiBufferLen = lstrlen ( pQuery->szBaseFileName ) + 1;
}
ReadRegistryIndirectStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCWSTR)L"Log File Base Name",
szDefault,
&pQuery->szBaseFileName,
&uiBufferLen );
ReplaceBlanksWithUnderscores ( pQuery->szBaseFileName );
if ( 0 == lstrlen (szDefault) ) {
if ( NULL != pQuery->szBaseFileName ) {
if ( 0 == lstrlen ( pQuery->szBaseFileName ) ) {
// Ignore bad status if the base log file name
//is NULL and auto format is enabled.
dwStatus = ERROR_SUCCESS;
}
} else {
// Ignore bad status if the base log file name
//is NULL and auto format is enabled.
dwStatus = ERROR_SUCCESS;
}
}
}
if ( ERROR_SUCCESS == dwStatus ) {
TCHAR* pszTemp = NULL;
DWORD cchLen = 0;
DWORD cchExpandedLen = 0;
dwStatus = ReadRegistryStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Log File Folder",
gszDefaultLogFileFolder,
&pszTemp,
&dwBufferSize );
//
// Parse all environment variables
//
if (pszTemp != NULL) {
cchLen = ExpandEnvironmentStrings ( pszTemp, NULL, 0 );
if ( 0 < cchLen ) {
//
// cchLen includes NULL.
//
if ( NULL != pQuery->szLogFileFolder ) {
G_FREE (pQuery->szLogFileFolder );
pQuery->szLogFileFolder = NULL;
}
pQuery->szLogFileFolder = G_ALLOC ( cchLen * sizeof(WCHAR) );
if ( NULL != pQuery->szLogFileFolder ) {
cchExpandedLen = ExpandEnvironmentStrings (
pszTemp,
pQuery->szLogFileFolder,
cchLen );
if ( 0 == cchExpandedLen ) {
dwStatus = GetLastError();
pQuery->szLogFileFolder[0] = L'\0';
}
} else {
dwStatus = ERROR_OUTOFMEMORY;
}
} else {
dwStatus = GetLastError();
}
}
if ( NULL != pszTemp ) {
G_FREE ( pszTemp );
}
}
if ( ERROR_SUCCESS == dwStatus ) {
ReadRegistryStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Sql Log Base Name",
NULL,
&pQuery->szSqlLogName,
&dwBufferSize );
// Ignore status, default is empty.
}
if ( ERROR_SUCCESS == dwStatus ) {
dwDefault = 1;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Log File Serial Number",
&dwDefault,
&pQuery->dwCurrentSerialNumber );
}
return dwStatus;
}
DWORD
LoadQueryConfig(
IN PLOG_QUERY_DATA pQuery )
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwBufferSize;
UINT uiBufferLen = 0;
LPTSTR szStringArray[2];
SLQ_TIME_INFO stiDefault;
SLQ_TIME_INFO stiTemp;
DWORD dwDefault;
DWORD dwType;
// Do not write event for invalid log type.
dwType = REG_DWORD;
dwBufferSize = sizeof(DWORD);
dwStatus = RegQueryValueExW (
pQuery->hKeyQuery,
(LPCTSTR)L"Log Type",
NULL,
&dwType,
(LPBYTE)&pQuery->dwLogType,
&dwBufferSize);
if ( SLQ_COUNTER_LOG == pQuery->dwLogType ) {
// Counters
dwStatus = ReadRegistryStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Counter List",
NULL,
&pQuery->mszCounterList,
&dwBufferSize );
if ( (ERROR_SUCCESS != dwStatus ) || ( 0 == dwBufferSize ) ) {
// no counter list retrieved so there's not much
// point in continuing
szStringArray[0] = pQuery->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_COUNTER_LIST,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
} else {
// Schedule
// Collect Command file value.
if ( ERROR_SUCCESS == dwStatus ) {
ReadRegistryStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"EOF Command File",
NULL,
&pQuery->szCmdFileName,
&dwBufferSize );
// Ignore status, default is empty.
}
if ( ERROR_SUCCESS == dwStatus ) {
stiDefault.wDataType = SLQ_TT_DTYPE_UNITS;
stiDefault.wTimeType = SLQ_TT_TTYPE_SAMPLE;
stiDefault.dwAutoMode = SLQ_AUTO_MODE_AFTER;
stiDefault.dwUnitType = SLQ_TT_UTYPE_SECONDS;
stiDefault.dwValue = 15;
dwStatus = ReadRegistrySlqTime (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Sample Interval",
&stiDefault,
&stiTemp);
if ( ERROR_SUCCESS == dwStatus ) {
LONGLONG llMillisecInterval;
TimeInfoToMilliseconds( &stiTemp, &llMillisecInterval );
assert ( ULONG_MAX > llMillisecInterval );
if ( ULONG_MAX > llMillisecInterval ) {
pQuery->dwMillisecondSampleInterval = (DWORD)(llMillisecInterval);
} else {
pQuery->dwMillisecondSampleInterval = ULONG_MAX - 1;
}
}
}
}
} else if ( SLQ_ALERT == pQuery->dwLogType) {
// Counters & alert limits
dwStatus = ReadRegistryStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Counter List",
NULL,
&pQuery->mszCounterList,
&dwBufferSize );
if ( (ERROR_SUCCESS != dwStatus ) || ( 0 == dwBufferSize ) ) {
// no counter list retrieved so there's not much
// point in continuing
szStringArray[0] = pQuery->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_COUNTER_LIST,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
} else {
// Schedule
if ( ERROR_SUCCESS == dwStatus ) {
stiDefault.wDataType = SLQ_TT_DTYPE_UNITS;
stiDefault.wTimeType = SLQ_TT_TTYPE_SAMPLE;
stiDefault.dwAutoMode = SLQ_AUTO_MODE_AFTER;
stiDefault.dwUnitType = SLQ_TT_UTYPE_SECONDS;
stiDefault.dwValue = 15;
dwStatus = ReadRegistrySlqTime (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Sample Interval",
&stiDefault,
&stiTemp);
if ( ERROR_SUCCESS == dwStatus ) {
LONGLONG llMillisecInterval;
TimeInfoToMilliseconds( &stiTemp, &llMillisecInterval );
assert ( ULONG_MAX > llMillisecInterval );
if ( ULONG_MAX > llMillisecInterval ) {
pQuery->dwMillisecondSampleInterval = (DWORD)(llMillisecInterval);
} else {
pQuery->dwMillisecondSampleInterval = ULONG_MAX - 1;
}
}
}
if ( ERROR_SUCCESS == dwStatus ) {
// get action flags
dwDefault = 0;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Action Flags",
&dwDefault,
&pQuery->dwAlertActionFlags);
}
if (( ERROR_SUCCESS == dwStatus ) &&
((pQuery->dwAlertActionFlags & ALRT_ACTION_SEND_MSG) != 0)) {
dwStatus = ReadRegistryStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Network Name",
(LPCTSTR)L"",
&pQuery->szNetName,
&dwBufferSize );
}
if (( ERROR_SUCCESS == dwStatus ) &&
((pQuery->dwAlertActionFlags & ALRT_ACTION_EXEC_CMD) != 0)) {
ReadRegistryStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Command File",
NULL,
&pQuery->szCmdFileName,
&dwBufferSize );
if (( ERROR_SUCCESS == dwStatus ) &&
((pQuery->dwAlertActionFlags & ALRT_CMD_LINE_U_TEXT) != 0)) {
// Todo: Log error events
if ( NULL == pQuery->szUserText ) {
uiBufferLen = 0;
} else {
uiBufferLen = lstrlen ( pQuery->szUserText ) + 1;
}
ReadRegistryIndirectStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"User Text",
(LPCTSTR)L"",
&pQuery->szUserText,
&uiBufferLen );
// Ignore status, default is empty.
}
}
if (( ERROR_SUCCESS == dwStatus ) &&
((pQuery->dwAlertActionFlags & ALRT_ACTION_START_LOG) != 0)) {
dwStatus = ReadRegistryStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Perf Log Name",
(LPCTSTR)L"",
&pQuery->szPerfLogName,
&dwBufferSize );
}
}
} else if ( SLQ_TRACE_LOG == pQuery->dwLogType ) {
// get trace log values
DWORD dwProviderStatus;
dwDefault = 0;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Trace Flags",
&dwDefault,
&pQuery->dwFlags);
dwProviderStatus = ReadRegistryStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Trace Provider List",
NULL,
&pQuery->mszProviderList,
&dwBufferSize );
if ( 0 == dwBufferSize ) {
if ( (ERROR_SUCCESS != dwProviderStatus )
&& ( ! IsKernelTraceMode( pQuery->dwFlags ) ) ) {
// No provider list retrieved and not kernel trace so there's not much
// point in continuing
if ( ERROR_SUCCESS == dwStatus ) {
dwStatus = SMLOG_UNABLE_READ_PROVIDER_LIST;
}
szStringArray[0] = pQuery->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_PROVIDER_LIST,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
} else {
// Allocate a minimal buffer for the NULL character to simplify later logic.
pQuery->mszProviderList = G_ALLOC ( sizeof(TCHAR) );
if ( NULL != pQuery->mszProviderList ) {
pQuery->mszProviderList[0] = _T('\0');
} else{
dwStatus = ERROR_OUTOFMEMORY;
szStringArray[0] = pQuery->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_PROVIDER_LIST,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
}
}
}
if ( ERROR_SUCCESS == dwStatus ) {
dwDefault = 4;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Trace Buffer Size",
&dwDefault,
&pQuery->dwBufferSize);
}
if ( ERROR_SUCCESS == dwStatus ) {
dwDefault = 2;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Trace Buffer Min Count",
&dwDefault,
&pQuery->dwBufferMinCount);
}
if ( ERROR_SUCCESS == dwStatus ) {
dwDefault = 25;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Trace Buffer Max Count",
&dwDefault,
&pQuery->dwBufferMaxCount);
}
if ( ERROR_SUCCESS == dwStatus ) {
dwDefault = 0;
dwStatus = ReadRegistryDwordValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Trace Buffer Flush Interval",
&dwDefault,
&pQuery->dwBufferFlushInterval);
}
// Schedule
// Collect Command file value.
// This is true for both Counter and Trace log files.
// Alerts use the Command file field for Alert command file.
if ( ERROR_SUCCESS == dwStatus ) {
ReadRegistryStringValue (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"EOF Command File",
NULL,
&pQuery->szCmdFileName,
&dwBufferSize );
// Ignore status, default is empty.
}
} else {
// Ignore partly created logs and alerts.
assert ( SLQ_NEW_LOG == pQuery->dwLogType );
if ( SLQ_NEW_LOG == pQuery->dwLogType ) {
dwStatus = SMLOG_LOG_TYPE_NEW;
} else {
dwStatus = SMLOG_INVALID_LOG_TYPE;
szStringArray[0] = pQuery->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_INVALID_LOG_TYPE,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&pQuery->dwLogType);
}
}
if ( ERROR_SUCCESS == dwStatus ) {
dwStatus = LoadCommonConfig ( pQuery );
}
return dwStatus;
}
void
LockQueryData ( void )
{
EnterCriticalSection ( &QueryDataLock );
}
void
UnlockQueryData ( void )
{
LeaveCriticalSection ( &QueryDataLock );
}
void
EnterConfigure ( void )
{
EnterCriticalSection ( &QueryDataLock );
}
void
ExitConfigure ( void )
{
LeaveCriticalSection ( &QueryDataLock );
}
PLOG_QUERY_DATA
GetQueryData (
LPCTSTR szQueryName )
{
PLOG_QUERY_DATA pQuery;
LockQueryData();
pQuery = pFirstQuery;
while ( NULL != pQuery ) {
if ( !lstrcmpi(pQuery->szQueryName, szQueryName ) ) {
// If the exit event isn't set, then this query is still active.
if ((WaitForSingleObject (pQuery->hExitEvent, 0)) != WAIT_OBJECT_0) {
break;
}
#if _DEBUG_OUTPUT
else {
{
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Exit event is set\n", pQuery->szQueryName);
OutputDebugString (szDebugString);
}
}
#endif
}
pQuery = pQuery->next;
}
UnlockQueryData();
return pQuery;
}
PLOG_QUERY_DATA
GetQueryDataPtr (
HANDLE hThisQuery
)
{
PLOG_QUERY_DATA pQuery = NULL;
LockQueryData();
// Find the query data block in the list.
if ( hThisQuery == pFirstQuery->hThread ) {
pQuery = pFirstQuery;
}
if ( NULL == pQuery ) {
for ( pQuery = pFirstQuery;
NULL != pQuery->next;
pQuery = pQuery->next ) {
if ( hThisQuery == pQuery->next->hThread ) {
pQuery = pQuery->next;
break;
}
}
}
UnlockQueryData();
return pQuery;
}
void
DeallocateQueryBuffers (
IN PLOG_QUERY_DATA pQuery )
{
// Deallocate the buffers that can be deleted when the collection
// thread is reconfigured.
if (( SLQ_COUNTER_LOG == pQuery->dwLogType ) ||
( SLQ_ALERT == pQuery->dwLogType)) {
if (pQuery->mszCounterList != NULL) {
G_FREE(pQuery->mszCounterList);
pQuery->mszCounterList = NULL;
}
}
if ( SLQ_ALERT == pQuery->dwLogType) {
if (pQuery->szNetName != NULL) {
G_FREE(pQuery->szNetName);
pQuery->szNetName = NULL;
}
if (pQuery->szPerfLogName != NULL) {
G_FREE(pQuery->szPerfLogName);
pQuery->szPerfLogName = NULL;
}
if (pQuery->szUserText != NULL) {
G_FREE (pQuery->szUserText);
pQuery->szUserText = NULL;
}
}
if ( SLQ_TRACE_LOG == pQuery->dwLogType) {
if (pQuery->mszProviderList != NULL) {
G_FREE(pQuery->mszProviderList);
pQuery->mszProviderList = NULL;
}
}
if (pQuery->szLogFileComment != NULL) {
G_FREE(pQuery->szLogFileComment);
pQuery->szLogFileComment = NULL;
}
if (pQuery->szBaseFileName != NULL) {
G_FREE(pQuery->szBaseFileName);
pQuery->szBaseFileName = NULL;
}
if (pQuery->szLogFileFolder != NULL) {
G_FREE(pQuery->szLogFileFolder);
pQuery->szLogFileFolder = NULL;
}
if (pQuery->szSqlLogName != NULL) {
G_FREE(pQuery->szSqlLogName);
pQuery->szSqlLogName = NULL;
}
if (pQuery->szCmdFileName != NULL) {
G_FREE(pQuery->szCmdFileName);
pQuery->szCmdFileName = NULL;
}
}
void
ClearTraceProperties (
IN PLOG_QUERY_DATA pQuery )
{
#if _IMPLEMENT_WMI
G_ZERO (& pQuery->Properties, sizeof(EVENT_TRACE_PROPERTIES));
G_ZERO (pQuery->szLoggerName, sizeof(pQuery->szLoggerName));
G_ZERO (pQuery->szLogFileName, sizeof(pQuery->szLogFileName));
if ( NULL != pQuery->arrpGuid ) {
ULONG ulIndex;
for ( ulIndex = 0; ulIndex < pQuery->ulGuidCount; ulIndex++ ) {
if ( NULL != pQuery->arrpGuid[ulIndex] ) {
G_FREE ( pQuery->arrpGuid[ulIndex] );
pQuery->arrpGuid[ulIndex] = NULL;
}
if ( NULL != pQuery->arrpszProviderName[ulIndex] ) {
G_FREE ( pQuery->arrpszProviderName[ulIndex] );
pQuery->arrpszProviderName[ulIndex] = NULL;
}
}
G_FREE ( pQuery->arrpGuid );
pQuery->arrpGuid = NULL;
if ( NULL != pQuery->arrpszProviderName ) {
G_FREE ( pQuery->arrpszProviderName );
pQuery->arrpszProviderName = NULL;
}
}
pQuery->ulGuidCount = 0;
pQuery->Properties.LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
pQuery->Properties.LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES)
+ sizeof(pQuery->szLoggerName);
#endif
}
DWORD
LoadPdhLogUpdateSuccess ( void )
{
DWORD dwStatus = ERROR_SUCCESS;
HKEY hKeySysmonLog = NULL;
dwStatus = RegOpenKeyEx (
(HKEY)HKEY_LOCAL_MACHINE,
(LPCTSTR)TEXT("SYSTEM\\CurrentControlSet\\Services\\SysmonLog"),
0L,
KEY_READ,
(PHKEY)&hKeySysmonLog);
if (dwStatus == ERROR_SUCCESS) {
TCHAR* mszStatusList = NULL;
DWORD dwBufferSize = 0;
DWORD dwType = 0;
// find out the size of the required buffer
dwStatus = RegQueryValueExW (
hKeySysmonLog,
(LPCTSTR)_T("PdhDataCollectSuccessStatus"),
NULL,
&dwType,
NULL,
&dwBufferSize); // In bytes
// If there is something to read
if ( (ERROR_SUCCESS == dwStatus ) && ( 0 < dwBufferSize ) ) {
mszStatusList = G_ALLOC ( dwBufferSize );
if ( NULL != mszStatusList ) {
mszStatusList[0] = _T('\0');
dwType = 0;
dwStatus = RegQueryValueExW (
hKeySysmonLog,
(LPCTSTR)_T("PdhDataCollectSuccessStatus"),
NULL,
&dwType,
(UCHAR*)mszStatusList,
&dwBufferSize);
if ( (ERROR_SUCCESS == dwStatus )
&& ( 0 < dwBufferSize )
&& ( _T('\0') != mszStatusList[0] ) ) {
// Allocate and load Pdh data collection status value array.
INT iStatusCount = 0;
TCHAR* szThisStatus;
for (szThisStatus = mszStatusList;
*szThisStatus != 0;
szThisStatus += lstrlen(szThisStatus) + 1) {
iStatusCount++;
}
arrPdhDataCollectSuccess = G_ALLOC ( iStatusCount * sizeof ( DWORD ) );
if ( NULL != arrPdhDataCollectSuccess ) {
INT iStatusIndex;
szThisStatus = mszStatusList;
for ( iStatusIndex = 0; iStatusIndex < iStatusCount; iStatusIndex++ ) {
if (0 != *szThisStatus ) {
arrPdhDataCollectSuccess[iStatusIndex] = (DWORD)_ttoi( szThisStatus );
szThisStatus += lstrlen(szThisStatus) + 1;
} else {
break;
}
}
}
iPdhDataCollectSuccessCount = iStatusCount;
}
if ( NULL != mszStatusList ) {
G_FREE ( mszStatusList );
}
} else {
dwStatus = ERROR_OUTOFMEMORY;
}
}
}
return dwStatus;
}
DWORD
InitTraceGuids(
IN PLOG_QUERY_DATA pQuery )
{
DWORD dwStatus = ERROR_SUCCESS;
#if _IMPLEMENT_WMI
LPTSTR pszThisGuid;
LONG ulGuidIndex;
LONG ulpszGuidIndex;
LONG ulGuidCount = 0;
LPGUID* arrpGuid = NULL;
PTCHAR* arrpszGuid = NULL;
WCHAR pszThisGuidBuffer[64];
UNICODE_STRING ustrGuid;
if ( NULL != pQuery ) {
if ( NULL != pQuery->mszProviderList ) {
for (pszThisGuid = pQuery->mszProviderList;
*pszThisGuid != 0;
pszThisGuid += lstrlen(pszThisGuid) + 1) {
ulGuidCount += 1;
if ( NULL == pszThisGuid ) {
dwStatus = ERROR_INVALID_PARAMETER;
break;
}
}
}
if ( ERROR_SUCCESS == dwStatus ) {
arrpGuid = G_ALLOC ( ulGuidCount * sizeof ( LPGUID ) );
if (NULL == arrpGuid) {
dwStatus = ERROR_OUTOFMEMORY;
} else {
G_ZERO ( arrpGuid, ulGuidCount * sizeof ( LPGUID ) );
for ( ulGuidIndex = 0; ulGuidIndex < ulGuidCount; ulGuidIndex++) {
arrpGuid[ulGuidIndex] = G_ALLOC ( sizeof(GUID) );
if (NULL == arrpGuid[ulGuidIndex]) {
dwStatus = ERROR_OUTOFMEMORY;
break;
}
}
}
}
if ( ERROR_SUCCESS == dwStatus ) {
// Create an array of pointers to individual provider Guids in the
// mszProviderList. The provider Guids are used as provider
// names in error messages, and for comparison with provider list
arrpszGuid = G_ALLOC ( ulGuidCount * sizeof ( TCHAR* ) );
if (NULL == arrpszGuid) {
dwStatus = ERROR_OUTOFMEMORY;
} else {
G_ZERO ( arrpszGuid, ulGuidCount * sizeof ( TCHAR* ) );
for ( ulpszGuidIndex = 0; ulpszGuidIndex < ulGuidCount; ulpszGuidIndex++) {
arrpszGuid[ulpszGuidIndex] = G_ALLOC ( sizeof(TCHAR[MAX_PATH]) );
if (NULL == arrpszGuid[ulpszGuidIndex]) {
dwStatus = ERROR_OUTOFMEMORY;
break;
}
}
}
if (ERROR_SUCCESS == dwStatus) {
ulGuidIndex = 0;
for (pszThisGuid = pQuery->mszProviderList;
*pszThisGuid != 0;
pszThisGuid += lstrlen(pszThisGuid) + 1) {
lstrcpyW ((LPWSTR)pszThisGuidBuffer, pszThisGuid);
ustrGuid.Length = (USHORT)(lstrlen(pszThisGuidBuffer)*sizeof(TCHAR)); // Size of GUID length << USHORT
ustrGuid.MaximumLength = sizeof (pszThisGuidBuffer);
ustrGuid.Buffer = pszThisGuidBuffer;
dwStatus = GUIDFromString (&ustrGuid, arrpGuid[ulGuidIndex] );
lstrcpy ( arrpszGuid[ulGuidIndex], pszThisGuid );
ulGuidIndex++;
}
pQuery->ulGuidCount = ulGuidCount;
pQuery->arrpGuid = arrpGuid;
pQuery->arrpszProviderName = arrpszGuid;
}
}
if (ERROR_SUCCESS != dwStatus) {
// If failure anywhere, deallocate arrays
if ( NULL != arrpszGuid ) {
for (ulpszGuidIndex--; ulpszGuidIndex>=0; ulpszGuidIndex--) {
G_FREE(arrpszGuid[ulpszGuidIndex]);
}
G_FREE(arrpszGuid);
}
if (NULL != arrpGuid) {
for (ulGuidIndex--; ulGuidIndex>=0; ulGuidIndex--) {
G_FREE(arrpGuid[ulGuidIndex]);
}
G_FREE(arrpGuid);
}
}
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
#else
dwStatus = ERROR_CALL_NOT_IMPLEMENTED;
#endif
return dwStatus;
}
DWORD
IsCreateNewFile (
IN PLOG_QUERY_DATA pQuery,
OUT BOOL* pbValidBySize,
OUT BOOL* pbValidByTime )
{
DWORD dwStatus = ERROR_SUCCESS;
BOOL bLocalValidBySize = FALSE;
BOOL bLocalValidByTime = FALSE;
if ( ( NULL != pQuery ) ) {
if ( SLQ_AUTO_MODE_SIZE == pQuery->stiCreateNewFile.dwAutoMode ) {
if ( ( SLF_SEQ_TRACE_FILE == pQuery->dwLogFileType )
&& ( -1 != pQuery->dwMaxFileSize )
&& ( 0 != pQuery->dwMaxFileSize ) )
{
bLocalValidBySize = TRUE;
}
} else if ( SLQ_AUTO_MODE_AFTER == pQuery->stiCreateNewFile.dwAutoMode ) {
bLocalValidByTime = TRUE;
}
if ( NULL != pbValidBySize ) {
*pbValidBySize = bLocalValidBySize;
}
if ( NULL != pbValidByTime ) {
*pbValidByTime = bLocalValidByTime;
}
} else {
assert ( FALSE );
dwStatus = ERROR_INVALID_PARAMETER;
}
return dwStatus;
}
void
InitTraceProperties (
IN PLOG_QUERY_DATA pQuery,
IN BOOL bUpdateSerial,
IN OUT DWORD* pdwSessionSerial,
IN OUT INT* piCnfSerial )
{
#if _IMPLEMENT_WMI
HRESULT hr;
DWORD dwStatus = ERROR_SUCCESS;
PPDH_PLA_INFO pInfo = NULL;
DWORD dwInfoSize = 0;
BOOL bBySize = FALSE;
BOOL bByTime = FALSE;
INT iLocalCnfSerial;
DWORD dwLocalSessionSerial = 0; // Init for Prefix check
BOOL bFileExists;
if ( NULL != pQuery && NULL != piCnfSerial ) {
hr = PdhPlaGetInfoW( pQuery->szQueryName, NULL, &dwInfoSize, pInfo );
if( ERROR_SUCCESS == hr && 0 != dwInfoSize ) {
pInfo = (PPDH_PLA_INFO)G_ALLOC(dwInfoSize);
if( NULL != pInfo && (sizeof(PDH_PLA_INFO) <= dwInfoSize) ){
ZeroMemory( pInfo, dwInfoSize );
pInfo->dwMask = PLA_INFO_FLAG_MODE|PLA_INFO_FLAG_LOGGERNAME;
hr = PdhPlaGetInfoW( pQuery->szQueryName, NULL, &dwInfoSize, pInfo );
}
}
ClearTraceProperties ( pQuery );
dwStatus = IsCreateNewFile ( pQuery, &bBySize, &bByTime );
// Create format string, store it in pQuery->szLogFileName
if ( bBySize ) {
// In BuildCurrentLogFileName, iCnfSerial of -1 signals code to
// return the format string for cnf serial number
iLocalCnfSerial = -1;
} else {
if ( bByTime ) {
*piCnfSerial += 1;
iLocalCnfSerial = *piCnfSerial;
} else {
iLocalCnfSerial = 0;
}
}
if ( NULL != pdwSessionSerial ) {
dwLocalSessionSerial = *pdwSessionSerial;
} else {
dwLocalSessionSerial = pQuery->dwCurrentSerialNumber;
}
dwStatus = BuildCurrentLogFileName (
pQuery->szQueryName,
pQuery->szBaseFileName,
pQuery->szLogFileFolder,
pQuery->szSqlLogName,
pQuery->szLogFileName,
&dwLocalSessionSerial,
pQuery->dwAutoNameFormat,
pQuery->dwLogFileType,
iLocalCnfSerial );
RegisterCurrentFile( pQuery->hKeyQuery, pQuery->szLogFileName, iLocalCnfSerial );
// Update log serial number if modified.
if ( bUpdateSerial && SLF_NAME_NNNNNN == pQuery->dwAutoNameFormat ) {
pQuery->dwCurrentSerialNumber++;
// Todo: Info event on number wrap - Server Beta 3.
if ( MAXIMUM_SERIAL_NUMBER < pQuery->dwCurrentSerialNumber ) {
pQuery->dwCurrentSerialNumber = MINIMUM_SERIAL_NUMBER;
}
WriteRegistryDwordValue (
pQuery->hKeyQuery,
(LPCTSTR)L"Log File Serial Number",
&pQuery->dwCurrentSerialNumber,
REG_DWORD);
// Todo: log event on error.
}
pQuery->Properties.Wnode.BufferSize = sizeof(pQuery->Properties)
+ sizeof(pQuery->szLoggerName)
+ sizeof(pQuery->szLogFileName);
if ( TRUE == bBySize ) {
// Add room for trace code to to return formatted filename string.
pQuery->Properties.Wnode.BufferSize += 8;
}
pQuery->Properties.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
// Fill out properties block and start.
pQuery->Properties.BufferSize = pQuery->dwBufferSize;
pQuery->Properties.MinimumBuffers = pQuery->dwBufferMinCount;
pQuery->Properties.MaximumBuffers = pQuery->dwBufferMaxCount;
if ( pInfo ) {
if ( pInfo->Trace.strLoggerName != NULL ) {
lstrcpy ( pQuery->szLoggerName, pInfo->Trace.strLoggerName );
}
} else {
lstrcpy ( pQuery->szLoggerName, pQuery->szQueryName );
}
if ( (BOOL)( 0 == (pQuery->dwFlags & SLQ_TLI_ENABLE_BUFFER_FLUSH)) )
pQuery->Properties.FlushTimer = 0;
else
pQuery->Properties.FlushTimer = pQuery->dwBufferFlushInterval;
if ( IsKernelTraceMode ( pQuery->dwFlags ) ) {
pQuery->Properties.Wnode.Guid = SystemTraceControlGuid;
lstrcpy ( pQuery->szLoggerName, NT_KERNEL_LOGGER );
if ( (BOOL)( 0 != (pQuery->dwFlags & SLQ_TLI_ENABLE_KERNEL_TRACE)) ) {
// NT5 Beta 2 Single Kernel flag
pQuery->Properties.EnableFlags |= EVENT_TRACE_FLAG_PROCESS |
EVENT_TRACE_FLAG_THREAD |
EVENT_TRACE_FLAG_DISK_IO |
EVENT_TRACE_FLAG_NETWORK_TCPIP;
} else {
if ( (BOOL)( 0 != (pQuery->dwFlags & SLQ_TLI_ENABLE_PROCESS_TRACE)) )
pQuery->Properties.EnableFlags |= EVENT_TRACE_FLAG_PROCESS;
if ( (BOOL)( 0 != (pQuery->dwFlags & SLQ_TLI_ENABLE_THREAD_TRACE)) )
pQuery->Properties.EnableFlags |= EVENT_TRACE_FLAG_THREAD;
if ( (BOOL)( 0 != (pQuery->dwFlags & SLQ_TLI_ENABLE_DISKIO_TRACE)) )
pQuery->Properties.EnableFlags |= EVENT_TRACE_FLAG_DISK_IO;
if ( (BOOL)( 0 != (pQuery->dwFlags & SLQ_TLI_ENABLE_NETWORK_TCPIP_TRACE)) )
pQuery->Properties.EnableFlags |= EVENT_TRACE_FLAG_NETWORK_TCPIP;
}
if ( (BOOL)( 0 != (pQuery->dwFlags & SLQ_TLI_ENABLE_MEMMAN_TRACE)) )
pQuery->Properties.EnableFlags |= EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS;
if ( (BOOL)( 0 != (pQuery->dwFlags & SLQ_TLI_ENABLE_FILEIO_TRACE)) )
pQuery->Properties.EnableFlags |= EVENT_TRACE_FLAG_DISK_FILE_IO;
} else {
InitTraceGuids ( pQuery );
}
if ( -1 == pQuery->dwMaxFileSize ) {
pQuery->Properties.MaximumFileSize = 0;
} else {
pQuery->Properties.MaximumFileSize = pQuery->dwMaxFileSize;
}
if ( ERROR_SUCCESS == dwStatus && TRUE == bBySize ) {
pQuery->Properties.LogFileMode =
EVENT_TRACE_FILE_MODE_SEQUENTIAL | EVENT_TRACE_FILE_MODE_NEWFILE;
} else if ( SLF_SEQ_TRACE_FILE == pQuery->dwLogFileType ) {
pQuery->Properties.LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL;
// Only set Append mode if the file already exists.
if ( pQuery->dwAppendMode && FileExists ( pQuery->szLogFileName ) ) {
pQuery->Properties.LogFileMode |= EVENT_TRACE_FILE_MODE_APPEND;
}
} else {
assert ( SLF_CIRC_TRACE_FILE == pQuery->dwLogFileType );
pQuery->Properties.LogFileMode = EVENT_TRACE_FILE_MODE_CIRCULAR;
}
if ( pInfo ) {
pQuery->Properties.LogFileMode |= pInfo->Trace.dwMode;
G_FREE( pInfo );
}
if ( NULL != pdwSessionSerial ) {
*pdwSessionSerial = dwLocalSessionSerial;
}
} // Todo: else report error, return error
#endif
}
void
FreeQueryData (
IN PLOG_QUERY_DATA pQuery )
{
// Caller must remove the thread data block from the list.
// Threads are deleted by only one thread, so this should not
// be deleted out from under.
assert ( NULL != pQuery );
if ( NULL != pQuery ) {
// Free this entry.
if (( SLQ_COUNTER_LOG == pQuery->dwLogType ) ||
( SLQ_ALERT == pQuery->dwLogType ) ){
while ( NULL != pQuery->pFirstCounter ) {
PLOG_COUNTER_INFO pDelCI = pQuery->pFirstCounter;
pQuery->pFirstCounter = pDelCI->next;
G_FREE( pDelCI );
}
} else {
if ( NULL != pQuery->arrpGuid ) {
ULONG ulIndex;
for ( ulIndex = 0; ulIndex < pQuery->ulGuidCount; ulIndex++ ) {
if ( NULL != pQuery->arrpGuid[ulIndex] ) {
G_FREE ( pQuery->arrpGuid[ulIndex] );
pQuery->arrpGuid[ulIndex] = NULL;
}
}
G_FREE ( pQuery->arrpGuid );
pQuery->arrpGuid = NULL;
}
}
if ( NULL != pQuery->hThread ) {
CloseHandle ( pQuery->hThread );
pQuery->hThread = NULL;
}
if ( NULL != pQuery->hUserToken ) {
CloseHandle ( pQuery->hUserToken );
pQuery->hUserToken = NULL;
}
if ( NULL != pQuery->hExitEvent ) {
CloseHandle ( pQuery->hExitEvent );
pQuery->hExitEvent = NULL;
}
if ( NULL != pQuery->hReconfigEvent ) {
CloseHandle ( pQuery->hReconfigEvent );
pQuery->hReconfigEvent = NULL;
}
if ( NULL != pQuery->hKeyQuery ) {
RegCloseKey ( pQuery->hKeyQuery );
pQuery->hKeyQuery = NULL;
}
DeallocateQueryBuffers( pQuery );
G_FREE (pQuery);
}
}
void
RemoveAndFreeQueryData (
HANDLE hThisQuery
)
{
PLOG_QUERY_DATA pQuery = NULL;
BOOL bFound = FALSE;
LockQueryData();
// Find the query data block and remove it from the list.
if ( hThisQuery == pFirstQuery->hThread ) {
bFound = TRUE;
}
if ( bFound ) {
pQuery = pFirstQuery;
pFirstQuery = pFirstQuery->next;
} else {
PLOG_QUERY_DATA pQueryRemaining;
for ( pQuery = pFirstQuery;
NULL != pQuery->next;
pQuery = pQuery->next ) {
if ( hThisQuery == pQuery->next->hThread ) {
pQueryRemaining = pQuery;
pQuery = pQuery->next;
pQueryRemaining->next = pQuery->next;
bFound = TRUE;
break;
}
}
}
assert ( bFound );
if ( bFound ) {
dwActiveSessionCount -= 1;
}
UnlockQueryData();
assert ( NULL != pQuery );
#if _DEBUG_OUTPUT
{
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Query removed\n", pQuery->szQueryName);
OutputDebugString (szDebugString);
}
#endif
if ( bFound ) {
FreeQueryData( pQuery );
}
}
LONGLONG
ComputeStartWaitTics(
IN PLOG_QUERY_DATA pQuery,
IN BOOL bWriteToRegistry
)
{
LONGLONG llWaitTics = ((LONGLONG)0);
LONGLONG llLocalDateTime;
LONGLONG llRptLocalDays = 0;
LONGLONG llRptStartTime = 0;
LONGLONG llRptStopTime = 0;
LONGLONG llRptLocalTime = 0;
SLQ_TIME_INFO stiSched;
// Compute time to wait before logging starts.
//
// Time returned is millisecond granularity.
//
// Return value:
//
// Start time minus Now when At time is in the future.
//
// 0 signals no wait. This is true when:
// Start is either Manual or At mode and start time set to before now.
// Exceptions for both of these cases are noted below.
//
// NULL_INTERVAL_TICS signals exit immediately. This is true when:
// Start is Manual and Start time is MAX_TIME_VALUE
// Stop is At mode and Stop time is past.
// Stop is Manual mode and Stop time is MIN_TIME_VALUE or any value <= Now
// Stop is After mode, After value is 0 (UI should protect against this).
// Stop is After mode, Start is At mode, stop time is past and repeat mode is Manual.
//
GetLocalFileTime (&llLocalDateTime);
if ( ( MAX_TIME_VALUE == pQuery->stiRegStart.llDateTime )
&& ( SLQ_AUTO_MODE_NONE == pQuery->stiRegStart.dwAutoMode ) ) {
// Manual Start, start time is MAX_TIME_VALUE
// Note: For repeat funcionality, manual start time might be > now.
// Need to keep the start mode Manual in this case to ensure that
// SetStoppedStatus works.
// Todo: Don't allow repeat or restart with Manual mode?
llWaitTics = NULL_INTERVAL_TICS;
} else if ( ( SLQ_AUTO_MODE_NONE == pQuery->stiRegStop.dwAutoMode )
&& ( pQuery->stiRegStop.llDateTime <= llLocalDateTime ) ) {
// Past Stop Manual time.
llWaitTics = NULL_INTERVAL_TICS;
} else if ( ( ( SLQ_AUTO_MODE_AT == pQuery->stiRegStop.dwAutoMode )
&& ( SLQ_AUTO_MODE_CALENDAR != pQuery->stiRepeat.dwAutoMode ) )
&& ( pQuery->stiRegStop.llDateTime <= llLocalDateTime ) ) {
// Past Stop At or time and repeat mode not set to calendar.
llWaitTics = NULL_INTERVAL_TICS;
} else if ( SLQ_AUTO_MODE_AFTER == pQuery->stiRegStop.dwAutoMode ) {
if ( 0 == pQuery->stiRegStop.dwValue ) {
// Stop After mode and value is 0.
llWaitTics = NULL_INTERVAL_TICS;
} else if ( ( SLQ_AUTO_MODE_AT == pQuery->stiRegStart.dwAutoMode )
&& ( SLQ_AUTO_MODE_NONE == pQuery->stiRepeat.dwAutoMode ) ) {
LONGLONG llTics;
TimeInfoToTics ( &pQuery->stiRegStop, &llTics );
if ( ( pQuery->stiRegStart.llDateTime + llTics ) < llLocalDateTime ) {
// Start at, Stop After modes, stop time is past and no restart.
llWaitTics = NULL_INTERVAL_TICS;
}
}
}
// This code writes to local start and stop time structures to compute
// start wait tics. This avoids excessive log stops and starts, since
// the original registry data structures are compared when the registry
// has been modified, to determine if a log config has been changed by the UI.
if ( NULL_INTERVAL_TICS != llWaitTics ) {
pQuery->stiCurrentStart = pQuery->stiRegStart;
pQuery->stiCurrentStop = pQuery->stiRegStop;
// Handle repeat option separately.
if ( SLQ_AUTO_MODE_CALENDAR == pQuery->stiRepeat.dwAutoMode ) {
assert ( SLQ_AUTO_MODE_AT == pQuery->stiCurrentStart.dwAutoMode );
assert ( SLQ_AUTO_MODE_AT == pQuery->stiCurrentStop.dwAutoMode );
// assert ( ( pQuery->stiCurrentStop.llDateTime - pQuery->stiCurrentStart.llDateTime )
// < (FILETIME_TICS_PER_SECOND * SECONDS_IN_DAY) );
if ( pQuery->stiCurrentStop.llDateTime <= llLocalDateTime ) {
llRptLocalDays = llLocalDateTime / (FILETIME_TICS_PER_SECOND * SECONDS_IN_DAY);
llRptLocalTime = llLocalDateTime - llRptLocalDays;
llRptStopTime = pQuery->stiCurrentStop.llDateTime
- ( pQuery->stiCurrentStop.llDateTime
/ (FILETIME_TICS_PER_SECOND * SECONDS_IN_DAY) );
pQuery->stiCurrentStop.llDateTime = llRptLocalDays + llRptStopTime;
if ( llRptStopTime < llRptLocalTime ) {
// Set to stop tomorrow.
pQuery->stiCurrentStop.llDateTime += (FILETIME_TICS_PER_SECOND * SECONDS_IN_DAY) ;
}
llRptStartTime = pQuery->stiCurrentStart.llDateTime
- ( pQuery->stiCurrentStart.llDateTime
/ (FILETIME_TICS_PER_SECOND * SECONDS_IN_DAY) );
pQuery->stiCurrentStart.llDateTime = llRptLocalDays + llRptStartTime;
if ( (pQuery->stiCurrentStop.llDateTime - pQuery->stiCurrentStart.llDateTime)
> (FILETIME_TICS_PER_SECOND * SECONDS_IN_DAY) ) {
// Set to start tomorrow.
pQuery->stiCurrentStart.llDateTime += (FILETIME_TICS_PER_SECOND * SECONDS_IN_DAY);
}
}
if ( bWriteToRegistry ) {
stiSched.wDataType = SLQ_TT_DTYPE_DATETIME;
stiSched.wTimeType = SLQ_TT_TTYPE_REPEAT_START;
stiSched.dwAutoMode = SLQ_AUTO_MODE_AT;
stiSched.llDateTime = pQuery->stiCurrentStart.llDateTime;
WriteRegistrySlqTime (
pQuery->hKeyQuery,
(LPCWSTR)L"Repeat Schedule Start",
&stiSched );
stiSched.wTimeType = SLQ_TT_TTYPE_REPEAT_STOP;
stiSched.dwAutoMode = SLQ_AUTO_MODE_AT;
stiSched.llDateTime = pQuery->stiCurrentStop.llDateTime;
WriteRegistrySlqTime (
pQuery->hKeyQuery,
(LPCWSTR)L"Repeat Schedule Stop",
&stiSched );
}
}
if ( pQuery->stiCurrentStart.llDateTime <= llLocalDateTime ) {
llWaitTics = ((LONGLONG)(0));
} else {
llWaitTics = pQuery->stiCurrentStart.llDateTime - llLocalDateTime;
}
// If manual mode, set the start time to now, to handle repeat schedule.
// If any thread other than the log thread accesses this field for a
// running query, then need to synchronize access to the field.
if( SLQ_AUTO_MODE_NONE == pQuery->stiCurrentStart.dwAutoMode
&& MIN_TIME_VALUE == pQuery->stiCurrentStart.llDateTime )
{
pQuery->stiCurrentStart.llDateTime = llLocalDateTime + llWaitTics;
}
}
return llWaitTics;
}
void
LoadDefaultLogFileFolder ( void )
{
HKEY hKeyLogService = NULL;
TCHAR szLocalPath[MAX_PATH+1] = TEXT("");
DWORD cchExpandedLen;
DWORD dwStatus;
dwStatus = RegOpenKeyEx (
(HKEY)HKEY_LOCAL_MACHINE,
(LPCTSTR)TEXT("SYSTEM\\CurrentControlSet\\Services\\SysmonLog"),
0L,
KEY_READ,
(PHKEY)&hKeyLogService);
// update the service status
ssSmLogStatus.dwCheckPoint++;
SetServiceStatus (hSmLogStatus, &ssSmLogStatus);
if (dwStatus == ERROR_SUCCESS) {
DWORD dwBufferSize = sizeof ( szLocalPath );
dwStatus = RegQueryValueExW (
hKeyLogService,
(LPCTSTR)L"DefaultLogFileFolder",
NULL,
0L,
(LPBYTE)szLocalPath,
&dwBufferSize);
RegCloseKey (hKeyLogService);
} // No message on error. Just use load the local default.
if ( 0 == lstrlen (szLocalPath ) ) {
lstrcpy ( szLocalPath, DEFAULT_LOG_FILE_FOLDER );
}
// Todo: local and global buffer sizes are fixed.
cchExpandedLen = ExpandEnvironmentStrings (
szLocalPath,
gszDefaultLogFileFolder,
MAX_PATH+1 );
if ( 0 == cchExpandedLen ) {
gszDefaultLogFileFolder[0] = L'\0';
}
}
DWORD
ProcessLogFileFolder (
IN PLOG_QUERY_DATA pQuery,
IN BOOL bReconfigure )
{
DWORD dwStatus = ERROR_SUCCESS;
TCHAR szLocalPath [MAX_PATH];
szLocalPath[0] = _T('\0');
if (GetFullPathName (
pQuery->szLogFileFolder,
MAX_PATH,
szLocalPath,
NULL) > 0) {
LPTSTR szEnd;
LPSECURITY_ATTRIBUTES lpSA = NULL;
TCHAR cBackslash = TEXT('\\');
szEnd = &szLocalPath[3];
if (*szEnd != 0) {
LONG lErrorMode;
lErrorMode = SetErrorMode ( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX );
// then there are sub dirs to create
while (*szEnd != 0) {
// go to next backslash
while ((*szEnd != cBackslash) && (*szEnd != 0)) szEnd++;
if (*szEnd == cBackslash) {
// terminate path here and create directory
*szEnd = 0;
if (!CreateDirectory (szLocalPath, lpSA)) {
// see what the error was and "adjust" it if necessary
dwStatus = GetLastError();
if ( ERROR_ALREADY_EXISTS == dwStatus ) {
// this is OK
dwStatus = ERROR_SUCCESS;
}
}
// replace backslash and go to next dir
*szEnd++ = cBackslash;
}
}
// create last dir in path now
if (!CreateDirectory (szLocalPath, lpSA)) {
// see what the error was and "adjust" it if necessary
dwStatus = GetLastError();
if ( ERROR_ALREADY_EXISTS == dwStatus ) {
// this is OK
dwStatus = ERROR_SUCCESS;
}
}
SetErrorMode ( lErrorMode );
} else {
// Root directory is ok.
dwStatus = ERROR_SUCCESS;
}
} else {
dwStatus = GetLastError();
}
// Report event on error
if ( ERROR_SUCCESS != dwStatus ) {
DWORD dwMessageId;
LPCWSTR szStringArray[3];
szStringArray[0] = pQuery->szLogFileFolder;
szStringArray[1] = pQuery->szQueryName;
szStringArray[2] = FormatEventLogMessage(dwStatus);
if ( bReconfigure ) {
dwMessageId = SMLOG_INVALID_LOG_FOLDER_STOP;
} else {
dwMessageId = SMLOG_INVALID_LOG_FOLDER_START;
}
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
dwMessageId,
NULL,
3,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
}
return dwStatus;
}
DWORD
OpenLogQueriesKey (
REGSAM regsamAccess,
PHKEY phKeyLogQueries )
{
DWORD dwStatus;
dwStatus = RegOpenKeyEx (
(HKEY)HKEY_LOCAL_MACHINE,
(LPCTSTR)TEXT("SYSTEM\\CurrentControlSet\\Services\\SysmonLog\\Log Queries"),
0L,
regsamAccess,
phKeyLogQueries);
return dwStatus;
}
DWORD
ClearQueryRunStates ( void )
{
DWORD dwStatus;
HKEY hKeyLogQueries = NULL;
HKEY hKeyThisLogQuery = NULL;
DWORD dwQueryIndex;
TCHAR szQueryNameBuffer[MAX_PATH+1];
DWORD dwQueryNameBufferSize;
TCHAR szQueryClassBuffer[MAX_PATH+1];
DWORD dwQueryClassBufferSize;
LPTSTR szCollectionName = NULL;
UINT uiCollectionNameLen = 0;
LPTSTR szStringArray[2];
DWORD dwCurrentState;
DWORD dwDefault;
DWORD dwLogType;
// For every query in the registry, if the state is SLQ_QUERY_RUNNING,
// set it to SLQ_QUERY_STOPPED.
//
// This method must be called before starting the query threads.
//
// Only the service sets the state to SLQ_QUERY_RUNNING, so there is no
// race condition.
// Open (each) query in the registry
dwStatus = OpenLogQueriesKey (
KEY_READ | KEY_SET_VALUE,
(PHKEY)&hKeyLogQueries);
if (dwStatus != ERROR_SUCCESS) {
if (dwStatus == ERROR_FILE_NOT_FOUND) {
// there is no logs nor alerts setting, bail out quietly
//
dwStatus = ERROR_SUCCESS;
}
else {
// unable to read the log query information from the registry
dwStatus = GetLastError();
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
SMLOG_UNABLE_OPEN_LOG_QUERY,
NULL,
0,
0,
NULL,
NULL);
dwStatus = SMLOG_UNABLE_OPEN_LOG_QUERY;
}
} else {
dwQueryIndex = 0;
*szQueryNameBuffer = 0;
dwQueryNameBufferSize = MAX_PATH+1;
*szQueryClassBuffer = 0;
dwQueryClassBufferSize = MAX_PATH+1;
while ((dwStatus = RegEnumKeyEx (
hKeyLogQueries,
dwQueryIndex,
szQueryNameBuffer,
&dwQueryNameBufferSize,
NULL,
szQueryClassBuffer,
&dwQueryClassBufferSize,
NULL)) != ERROR_NO_MORE_ITEMS) {
// open this key
dwStatus = RegOpenKeyEx (
hKeyLogQueries,
szQueryNameBuffer,
0L,
KEY_READ | KEY_WRITE,
(PHKEY)&hKeyThisLogQuery);
if (dwStatus != ERROR_SUCCESS) {
szStringArray[0] = szQueryNameBuffer;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_LOG_QUERY,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
// skip to next item
goto CONTINUE_ENUM_LOOP;
}
// update the service status
ssSmLogStatus.dwCheckPoint++;
SetServiceStatus (hSmLogStatus, &ssSmLogStatus);
dwStatus = SmReadRegistryIndirectStringValue (
hKeyThisLogQuery,
L"Collection Name",
NULL,
&szCollectionName,
&uiCollectionNameLen );
if ( NULL != szCollectionName ) {
if ( 0 < lstrlen ( szCollectionName ) ) {
lstrcpyn (
szQueryNameBuffer,
szCollectionName,
min(MAX_PATH, lstrlen(szCollectionName)+1 ) );
}
G_FREE ( szCollectionName );
szCollectionName = NULL;
uiCollectionNameLen = 0;
}
dwDefault = ((DWORD)-1);
dwStatus = ReadRegistryDwordValue (
hKeyThisLogQuery,
szQueryNameBuffer,
(LPCTSTR)L"Log Type",
&dwDefault,
&dwLogType );
if ( ( SLQ_COUNTER_LOG == dwLogType )
|| ( SLQ_TRACE_LOG == dwLogType )
|| ( SLQ_ALERT == dwLogType ) ) {
// Check the current state of the query. If it is SLQ_QUERY_RUNNING,
// set it to SLQ_QUERY_STOPPED. If, in addition, the Start mode is
// manual, set the start time to MAX, so that the query doesn't
// start automatically.
// If the current state is SLQ_QUERY_START_PENDING, it is assumed to be a new
// request, so leave the registry as is.
//
// Note: For trace logs, this code only coordinates between trace log
// configs that are stored in the registry.
dwDefault = SLQ_QUERY_STOPPED;
dwStatus = ReadRegistryDwordValue (
hKeyThisLogQuery,
szQueryNameBuffer,
(LPCTSTR)L"Current State",
&dwDefault,
&dwCurrentState );
assert (dwStatus == ERROR_SUCCESS);
// Status always success if default provided.
// If query is in START_PENDING or STOPPED state, then
// the registry contents are correct. If it is in
// RUNNING state, then the service was stopped before
// it could clean up the registry state.
if ( SLQ_QUERY_RUNNING == dwCurrentState ) {
SLQ_TIME_INFO stiDefault;
SLQ_TIME_INFO stiActual;
LONGLONG ftLocalTime;
dwCurrentState = SLQ_QUERY_STOPPED;
dwStatus = WriteRegistryDwordValue (
hKeyThisLogQuery,
(LPCTSTR)L"Current State",
&dwCurrentState,
REG_DWORD );
if (dwStatus != ERROR_SUCCESS) {
szStringArray[0] = szQueryNameBuffer;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_WRITE_STOP_STATE,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
// skip to next item
goto CONTINUE_ENUM_LOOP;
}
// If Start is manual mode, set start time to MAX, to signal
// not started.
GetLocalFileTime ( &ftLocalTime );
stiDefault.wTimeType = SLQ_TT_TTYPE_START;
stiDefault.dwAutoMode = SLQ_AUTO_MODE_AT;
stiDefault.wDataType = SLQ_TT_DTYPE_DATETIME;
stiDefault.llDateTime = *(LONGLONG *)&ftLocalTime;
dwStatus = ReadRegistrySlqTime (
hKeyThisLogQuery,
szQueryNameBuffer,
(LPCTSTR)L"Start",
&stiDefault,
&stiActual );
assert (dwStatus == ERROR_SUCCESS);
// Status always success if default provided.
if ( ( SLQ_AUTO_MODE_NONE == stiActual.dwAutoMode )
&& ( MAX_TIME_VALUE != stiActual.llDateTime ) ) {
stiActual.llDateTime = MAX_TIME_VALUE;
dwStatus = WriteRegistrySlqTime (
hKeyThisLogQuery,
(LPCTSTR)L"Start",
&stiActual);
if (dwStatus != ERROR_SUCCESS) {
szStringArray[0] = szQueryNameBuffer;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_RESET_START_TIME,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
// skip to next item
goto CONTINUE_ENUM_LOOP;
}
}
// If Stop is manual mode, set stop time to MIN, to signal
// not started.
GetLocalFileTime ( &ftLocalTime );
stiDefault.wDataType = SLQ_TT_DTYPE_DATETIME;
stiDefault.wTimeType = SLQ_TT_TTYPE_STOP;
stiDefault.dwAutoMode = SLQ_AUTO_MODE_NONE;
stiDefault.llDateTime = MIN_TIME_VALUE;
dwStatus = ReadRegistrySlqTime (
hKeyThisLogQuery,
szQueryNameBuffer,
(LPCTSTR)L"Stop",
&stiDefault,
&stiActual );
assert (dwStatus == ERROR_SUCCESS);
// Status always success if default provided.
if ( ( SLQ_AUTO_MODE_NONE == stiActual.dwAutoMode )
&& ( MIN_TIME_VALUE != stiActual.llDateTime ) ) {
stiActual.llDateTime = MIN_TIME_VALUE;
dwStatus = WriteRegistrySlqTime (
hKeyThisLogQuery,
(LPCTSTR)L"Stop",
&stiActual);
if (dwStatus != ERROR_SUCCESS) {
szStringArray[0] = szQueryNameBuffer;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_RESET_STOP_TIME,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
// skip to next item
goto CONTINUE_ENUM_LOOP;
}
}
}
} // Ignore invalid log types when clearing status.
CONTINUE_ENUM_LOOP:
if ( NULL != hKeyThisLogQuery )
RegCloseKey (hKeyThisLogQuery);
hKeyThisLogQuery = NULL;
// prepare for next loop
dwQueryIndex++;
*szQueryNameBuffer = 0;
dwQueryNameBufferSize = MAX_PATH+1;
*szQueryClassBuffer = 0;
dwQueryClassBufferSize = MAX_PATH+1;
} // end enumeration of log queries
}
if ( NULL != hKeyLogQueries ) {
RegCloseKey (hKeyLogQueries);
}
return dwStatus;
}
BOOL
TraceStopRestartFieldsMatch (
IN PLOG_QUERY_DATA pOrigQuery,
IN PLOG_QUERY_DATA pNewQuery )
{
#if _IMPLEMENT_WMI
// These are fields for which trace logging must
// be stopped and restarted in order to reconfigure.
BOOL bRequested;
BOOL bCurrent;
ULONG ulGuidCount = 0;
ULONG ulGuidIndex = 0;
TCHAR* pszThisGuid = NULL;
assert ( SLQ_TRACE_LOG == pOrigQuery->dwLogType );
assert ( SLQ_TRACE_LOG == pNewQuery->dwLogType );
if ( !CommonFieldsMatch ( pOrigQuery, pNewQuery ) )
return FALSE;
if ( pOrigQuery->stiCreateNewFile.dwAutoMode != pNewQuery->stiCreateNewFile.dwAutoMode ) {
return FALSE;
} else {
if ( ( SLQ_AUTO_MODE_AFTER == pOrigQuery->stiCreateNewFile.dwAutoMode )
&& ( pOrigQuery->stiCreateNewFile.llDateTime != pNewQuery->stiCreateNewFile.llDateTime ) ) {
return FALSE;
}
}
// Compare new query fields against the existing properties structure.
// Compare everything but flush interval, max buffer count and file name.
if ( pOrigQuery->Properties.BufferSize != pNewQuery->dwBufferSize )
return FALSE;
if ( pOrigQuery->Properties.MinimumBuffers != pNewQuery->dwBufferMinCount )
return FALSE;
// Not kernel trace, so check query name
if ((BOOL)( 0 == ( pNewQuery->dwFlags & SLQ_TLI_ENABLE_KERNEL_TRACE ) ) ) {
if ( 0 != lstrcmpi ( pOrigQuery->szLoggerName, pNewQuery->szQueryName ) ) {
return FALSE;
}
}
bRequested = (BOOL)( 0 != ( pNewQuery->dwFlags & SLQ_TLI_ENABLE_KERNEL_TRACE ) );
bCurrent = IsEqualGUID( &pOrigQuery->Properties.Wnode.Guid, &SystemTraceControlGuid );
if ( bRequested != bCurrent ) {
return FALSE;
}
// Extended memory trace
bRequested = (BOOL)( 0 != ( pNewQuery->dwFlags & SLQ_TLI_ENABLE_MEMMAN_TRACE ) );
bCurrent = (BOOL)( 0 != ( pOrigQuery->Properties.EnableFlags & EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS ) );
if ( bRequested != bCurrent ) {
return FALSE;
}
// Extended I/O trace
bRequested = (BOOL)( 0 != ( pNewQuery->dwFlags & SLQ_TLI_ENABLE_FILEIO_TRACE ) );
bCurrent = (BOOL)( 0 != ( pOrigQuery->Properties.EnableFlags & EVENT_TRACE_FLAG_DISK_FILE_IO ) );
if ( bRequested != bCurrent ) {
return FALSE;
}
if ( -1 == pNewQuery->dwMaxFileSize ) {
if ( 0 != pOrigQuery->Properties.MaximumFileSize ) {
return FALSE;
}
} else if ( pOrigQuery->Properties.MaximumFileSize != pNewQuery->dwMaxFileSize ) {
return FALSE;
}
if ( ( SLF_SEQ_TRACE_FILE == pNewQuery->dwLogFileType )
&& ( EVENT_TRACE_FILE_MODE_SEQUENTIAL != pOrigQuery->Properties.LogFileMode ) ) {
return FALSE;
} else if ( ( SLF_CIRC_TRACE_FILE == pNewQuery->dwLogFileType )
&& ( EVENT_TRACE_FILE_MODE_CIRCULAR != pOrigQuery->Properties.LogFileMode ) ) {
return FALSE;
}
// Compare each provider string against array element.
for (pszThisGuid = pNewQuery->mszProviderList;
*pszThisGuid != 0;
pszThisGuid += lstrlen(pszThisGuid) + 1) {
ulGuidCount += 1;
}
if ( pOrigQuery->ulGuidCount != ulGuidCount )
return FALSE;
ulGuidIndex = 0;
for (pszThisGuid = pNewQuery->mszProviderList;
*pszThisGuid != 0;
pszThisGuid += lstrlen(pszThisGuid) + 1) {
if ( 0 != lstrcmpi ( pOrigQuery->arrpszProviderName[ulGuidIndex], pszThisGuid ) )
return FALSE;
ulGuidIndex++;
assert ( ulGuidIndex <= ulGuidCount );
}
return TRUE;
#else
return FALSE;
#endif
}
BOOL
AlertFieldsMatch (
IN PLOG_QUERY_DATA pFirstQuery,
IN PLOG_QUERY_DATA pSecondQuery )
{
if ( pFirstQuery->dwAlertActionFlags != pSecondQuery->dwAlertActionFlags )
return FALSE;
if ( 0 != (pFirstQuery->dwAlertActionFlags & ALRT_ACTION_SEND_MSG) ) {
if ( 0 != lstrcmpi ( pFirstQuery->szNetName, pSecondQuery->szNetName ) ) {
return FALSE;
}
}
if ( 0 != (pFirstQuery->dwAlertActionFlags & ALRT_ACTION_EXEC_CMD) ) {
if ( 0 != lstrcmpi ( pFirstQuery->szCmdFileName, pSecondQuery->szCmdFileName ) ) {
return FALSE;
}
if ( 0 != (pFirstQuery->dwAlertActionFlags & ALRT_CMD_LINE_U_TEXT ) ) {
if ( 0 != lstrcmpi ( pFirstQuery->szUserText, pSecondQuery->szUserText ) ) {
return FALSE;
}
}
}
if ( 0 != (pFirstQuery->dwAlertActionFlags & ALRT_ACTION_START_LOG) ) {
if ( 0 != lstrcmpi ( pFirstQuery->szPerfLogName, pSecondQuery->szPerfLogName ) ) {
return FALSE;
}
}
return TRUE;
}
BOOL
CommonFieldsMatch (
IN PLOG_QUERY_DATA pFirstQuery,
IN PLOG_QUERY_DATA pSecondQuery )
{
if ( pFirstQuery->dwCurrentState != pSecondQuery->dwCurrentState )
return FALSE;
if ( pFirstQuery->dwLogFileType != pSecondQuery->dwLogFileType )
return FALSE;
if ( pFirstQuery->dwAutoNameFormat != pSecondQuery->dwAutoNameFormat )
return FALSE;
if ( pFirstQuery->dwMaxFileSize != pSecondQuery->dwMaxFileSize )
return FALSE;
if ( pFirstQuery->stiRegStart.dwAutoMode != pSecondQuery->stiRegStart.dwAutoMode )
return FALSE;
if ( pFirstQuery->stiRegStop.dwAutoMode != pSecondQuery->stiRegStop.dwAutoMode )
return FALSE;
if ( pFirstQuery->stiRepeat.dwAutoMode != pSecondQuery->stiRepeat.dwAutoMode )
return FALSE;
if ( pFirstQuery->stiRegStart.llDateTime != pSecondQuery->stiRegStart.llDateTime )
return FALSE;
if ( pFirstQuery->stiRegStop.llDateTime != pSecondQuery->stiRegStop.llDateTime )
return FALSE;
if ( pFirstQuery->stiRepeat.llDateTime != pSecondQuery->stiRepeat.llDateTime )
return FALSE;
if (( SLQ_COUNTER_LOG == pFirstQuery->dwLogType ) ||
( SLQ_TRACE_LOG == pFirstQuery->dwLogType)) {
if ( 0 != lstrcmpi ( pFirstQuery->szBaseFileName, pSecondQuery->szBaseFileName ) )
return FALSE;
if ( 0 != lstrcmpi ( pFirstQuery->szLogFileFolder, pSecondQuery->szLogFileFolder ) )
return FALSE;
if ( 0 != lstrcmpi ( pFirstQuery->szSqlLogName, pSecondQuery->szSqlLogName ) )
return FALSE;
if ( 0 != lstrcmpi ( pFirstQuery->szLogFileComment, pSecondQuery->szLogFileComment ) )
return FALSE;
if ( pFirstQuery->dwCurrentSerialNumber != pSecondQuery->dwCurrentSerialNumber )
return FALSE;
if ( pFirstQuery->dwLogFileSizeUnit != pSecondQuery->dwLogFileSizeUnit )
return FALSE;
if ( pFirstQuery->dwAppendMode != pSecondQuery->dwAppendMode )
return FALSE;
if ( pFirstQuery->stiCreateNewFile.dwAutoMode != pSecondQuery->stiCreateNewFile.dwAutoMode )
return FALSE;
if ( pFirstQuery->stiCreateNewFile.llDateTime != pSecondQuery->stiCreateNewFile.llDateTime )
return FALSE;
if ( 0 != lstrcmpi(pFirstQuery->szCmdFileName, pSecondQuery->szCmdFileName ) )
return FALSE;
}
if (( SLQ_COUNTER_LOG == pFirstQuery->dwLogType ) ||
( SLQ_ALERT == pFirstQuery->dwLogType)) {
LPTSTR szFirstPath;
LPTSTR szSecondPath;
if ( pFirstQuery->dwMillisecondSampleInterval != pSecondQuery->dwMillisecondSampleInterval ) {
return FALSE;
}
// Compare each counter string. Note: If counter order has changed, the query is
// reconfigured.
// For Alert queries, this code also checks the limit threshold logic and value.
szSecondPath = pSecondQuery->mszCounterList;
for ( szFirstPath = pFirstQuery->mszCounterList;
*szFirstPath != 0;
szFirstPath += lstrlen(szFirstPath) + 1) {
if ( 0 != lstrcmpi( szFirstPath, szSecondPath ) ) {
return FALSE;
}
szSecondPath += lstrlen(szSecondPath) + 1;
}
if ( 0 != *szSecondPath ) {
return FALSE;
}
}
return TRUE;
}
BOOL
FieldsMatch (
IN PLOG_QUERY_DATA pFirstQuery,
IN PLOG_QUERY_DATA pSecondQuery )
{
assert ( pFirstQuery->dwLogType == pSecondQuery->dwLogType );
if ( !CommonFieldsMatch ( pFirstQuery, pSecondQuery ) )
return FALSE;
if ( SLQ_ALERT == pFirstQuery->dwLogType ) {
if ( !AlertFieldsMatch( pFirstQuery, pSecondQuery ) ) {
return FALSE;
}
} else if ( SLQ_TRACE_LOG == pFirstQuery->dwLogType ) {
LPTSTR szFirstProv;
LPTSTR szSecondProv;
if ( pFirstQuery->dwBufferSize != pSecondQuery->dwBufferSize )
return FALSE;
if ( pFirstQuery->dwBufferMinCount != pSecondQuery->dwBufferMinCount )
return FALSE;
if ( pFirstQuery->dwBufferMaxCount != pSecondQuery->dwBufferMaxCount )
return FALSE;
if ( pFirstQuery->dwBufferFlushInterval != pSecondQuery->dwBufferFlushInterval )
return FALSE;
if ( pFirstQuery->dwFlags != pSecondQuery->dwFlags )
return FALSE;
szSecondProv = pSecondQuery->mszProviderList;
for ( szFirstProv = pFirstQuery->mszProviderList;
*szFirstProv != 0;
szFirstProv += lstrlen(szFirstProv) + 1) {
if ( 0 != lstrcmpi ( szFirstProv, szSecondProv ) )
return FALSE;
szSecondProv += lstrlen(szSecondProv) + 1;
}
if ( 0 != *szSecondProv) {
return FALSE;
}
} else if ( SLQ_COUNTER_LOG == pFirstQuery->dwLogType ) {
if ( pFirstQuery->stiCreateNewFile.dwAutoMode != pSecondQuery->stiCreateNewFile.dwAutoMode ) {
return FALSE;
} else {
if ( SLQ_AUTO_MODE_AFTER == pFirstQuery->stiCreateNewFile.dwAutoMode
&& pFirstQuery->stiCreateNewFile.llDateTime != pSecondQuery->stiCreateNewFile.llDateTime ) {
return FALSE;
} // else change in max size handled in commmon fields match check.
}
}
return TRUE;
}
DWORD
IsModified (
IN PLOG_QUERY_DATA pQuery,
OUT BOOL* pbModified
)
{
DWORD dwStatus;
SLQ_TIME_INFO stiLastModified;
SLQ_TIME_INFO stiDefault;
*pbModified = TRUE;
// Check the last read date against 'last modified' in
// the registry.
// If it is earlier than the registry, and the data in the
// registry has changed, return TRUE.
//
// The check of thread data against registry data reduces the
// number of times that the logging thread is interrupted.
// This is necessary because each property page OnApply
// generates this check.
//
stiDefault.wDataType = SLQ_TT_DTYPE_DATETIME;
stiDefault.wTimeType = SLQ_TT_TTYPE_LAST_MODIFIED;
stiDefault.dwAutoMode = SLQ_AUTO_MODE_AT;
stiDefault.llDateTime = MAX_TIME_VALUE;
dwStatus = ReadRegistrySlqTime (
pQuery->hKeyQuery,
pQuery->szQueryName,
(LPCTSTR)L"Last Modified",
&stiDefault,
&stiLastModified );
assert( ERROR_SUCCESS == dwStatus );
// Status always success if default provided.
if ( stiLastModified.llDateTime <= pQuery->llLastConfigured ) {
*pbModified = FALSE;
} else {
LOG_QUERY_DATA TempQuery;
memset (&TempQuery, 0, sizeof(TempQuery));
lstrcpy (TempQuery.szQueryName, pQuery->szQueryName);
TempQuery.hKeyQuery = pQuery->hKeyQuery;
if ( ERROR_SUCCESS != LoadQueryConfig( &TempQuery ) ) {
// Event has been logged. Set mod flag to stop the query.
*pbModified = TRUE;
} else {
*pbModified = !FieldsMatch ( pQuery, &TempQuery );
}
// Delete memory allocated by registry data load.
DeallocateQueryBuffers ( &TempQuery );
}
return dwStatus;
}
DWORD
ReconfigureQuery (
IN PLOG_QUERY_DATA pQuery )
{
DWORD dwStatus = ERROR_SUCCESS;
// *** Optimization - perform this check within IsModified, to avoid extra
// load from the registry.
LOG_QUERY_DATA TempQuery;
BOOL bStopQuery = FALSE;
memset (&TempQuery, 0, sizeof(TempQuery));
lstrcpy (TempQuery.szQueryName, pQuery->szQueryName);
TempQuery.hKeyQuery = pQuery->hKeyQuery;
if ( ERROR_SUCCESS != LoadQueryConfig( &TempQuery ) ) {
// Event has been logged. Stop the query.
bStopQuery = TRUE;
} else {
bStopQuery = ( NULL_INTERVAL_TICS == ComputeStartWaitTics ( &TempQuery, FALSE ) );
}
if ( !bStopQuery ) {
if ( SLQ_TRACE_LOG == pQuery->dwLogType
|| SLQ_COUNTER_LOG == pQuery->dwLogType ) {
// Stop the query if new log file folder is not valid.
bStopQuery = ( ERROR_SUCCESS != ProcessLogFileFolder( &TempQuery, TRUE ) );
}
}
if ( bStopQuery ) {
#if _DEBUG_OUTPUT
{
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Set exit event\n", pQuery->szQueryName);
OutputDebugString (szDebugString);
}
#endif
SetEvent (pQuery->hExitEvent);
} else {
if (( SLQ_COUNTER_LOG == pQuery->dwLogType ) ||
( SLQ_ALERT == pQuery->dwLogType ) ){
// Signal the logging thread to reconfigure.
pQuery->bLoadNewConfig= TRUE;
SetEvent (pQuery->hReconfigEvent);
} else {
#if _IMPLEMENT_WMI
BOOL bMustStopRestart;
assert( SLQ_TRACE_LOG == pQuery->dwLogType );
//
// Change the current query. For some properties, this
// means stopping then restarting the query.
//
bMustStopRestart = !TraceStopRestartFieldsMatch ( pQuery, &TempQuery );
if ( !bMustStopRestart ) {
if ( ERROR_SUCCESS != LoadQueryConfig( pQuery ) ) {
SetEvent (pQuery->hExitEvent);
} else {
// Update the trace log session. Do not increment
// the file autoformat serial number.
// Todo: File name serial number is already incremented.
InitTraceProperties ( pQuery, FALSE, NULL, NULL );
dwStatus = GetTraceQueryStatus ( pQuery, NULL );
if ( ERROR_SUCCESS == dwStatus ) {
dwStatus = UpdateTrace(
pQuery->LoggerHandle,
pQuery->szLoggerName,
&pQuery->Properties );
}
}
} else {
// Signal the logging thread to reconfigure.
pQuery->bLoadNewConfig= TRUE;
SetEvent (pQuery->hReconfigEvent);
}
}
}
#else
dwStatus = ERROR_CALL_NOT_IMPLEMENTED;
#endif
return dwStatus;
}
DWORD
GetTraceQueryStatus (
IN PLOG_QUERY_DATA pQuery,
IN OUT PLOG_QUERY_DATA pReturnQuery )
{
DWORD dwStatus = ERROR_SUCCESS;
#if _IMPLEMENT_WMI
PLOG_QUERY_DATA pLocalQuery = NULL;
if ( NULL != pQuery ) {
if ( NULL != pReturnQuery ) {
pLocalQuery = pReturnQuery;
} else {
pLocalQuery = G_ALLOC ( sizeof (LOG_QUERY_DATA) );
}
if ( NULL != pLocalQuery ) {
ClearTraceProperties ( pLocalQuery );
pLocalQuery->Properties.Wnode.BufferSize = sizeof(pQuery->Properties)
+ sizeof(pQuery->szLoggerName)
+ sizeof(pQuery->szLogFileName);
pLocalQuery->Properties.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
dwStatus = QueryTrace (
pQuery->LoggerHandle,
pQuery->szLoggerName,
&pLocalQuery->Properties );
if ( NULL == pReturnQuery ) {
G_FREE ( pLocalQuery );
}
} else {
dwStatus = ERROR_OUTOFMEMORY;
}
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
#else
dwStatus = ERROR_CALL_NOT_IMPLEMENTED;
#endif
return dwStatus;
}
DWORD
StartQuery (
IN PLOG_QUERY_DATA pQuery )
{
DWORD dwStatus = ERROR_SUCCESS;
LPTSTR szStringArray[2];
HANDLE hThread = NULL;
DWORD dwThreadId;
pQuery->bLoadNewConfig= FALSE;
// Create the logging thread.
hThread = CreateThread (
NULL, 0, LoggingThreadProc,
(LPVOID)pQuery, 0, &dwThreadId);
if ( NULL != hThread ) {
pQuery->hThread = hThread;
} else {
// unable to start thread
dwStatus = GetLastError();
szStringArray[0] = pQuery->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_START_THREAD,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
}
if ( ERROR_SUCCESS != dwStatus ) {
SetStoppedStatus ( pQuery );
}
return dwStatus;
}
DWORD
SetStoppedStatus (
IN PLOG_QUERY_DATA pQuery )
{
DWORD dwStatus;
SYSTEMTIME st;
LONGLONG llTime;
pQuery->dwCurrentState = SLQ_QUERY_STOPPED;
dwStatus = WriteRegistryDwordValue (
pQuery->hKeyQuery,
(LPCTSTR)L"Current State",
&pQuery->dwCurrentState,
REG_DWORD );
if ( SLQ_AUTO_MODE_NONE == pQuery->stiRegStart.dwAutoMode ) {
pQuery->stiRegStart.llDateTime = MAX_TIME_VALUE;
dwStatus = WriteRegistrySlqTime (
pQuery->hKeyQuery,
(LPCTSTR)L"Start",
&pQuery->stiRegStart);
}
GetLocalTime(&st);
SystemTimeToFileTime (&st, (FILETIME *)&llTime);
// If stop is manual or StopAt with time before now (no repeat), set to manual
// with MIN_TIME_VALUE
if ( SLQ_AUTO_MODE_NONE == pQuery->stiRegStop.dwAutoMode
&& llTime >= pQuery->stiRegStop.llDateTime )
{
pQuery->stiRegStop.dwAutoMode = SLQ_AUTO_MODE_NONE;
pQuery->stiRegStop.llDateTime = MIN_TIME_VALUE;
dwStatus = WriteRegistrySlqTime (
pQuery->hKeyQuery,
(LPCTSTR)L"Stop",
&pQuery->stiRegStop);
} else if ( ( SLQ_AUTO_MODE_AT == pQuery->stiRegStop.dwAutoMode
&& ( SLQ_AUTO_MODE_CALENDAR != pQuery->stiRepeat.dwAutoMode ) )
&& ( llTime >= pQuery->stiRegStop.llDateTime ) ) {
pQuery->stiRegStop.dwAutoMode = SLQ_AUTO_MODE_NONE;
pQuery->stiRegStop.llDateTime = MIN_TIME_VALUE;
dwStatus = WriteRegistrySlqTime (
pQuery->hKeyQuery,
(LPCTSTR)L"Stop",
&pQuery->stiRegStop);
}
return dwStatus;
}
DWORD
HandleMaxQueriesExceeded (
IN PLOG_QUERY_DATA pQuery )
{
DWORD dwStatus = ERROR_SUCCESS;
// The query has not been started yet, but still in "Start Pending" state.
SetStoppedStatus ( pQuery );
return dwStatus;
}
DWORD
ConfigureQuery (
HKEY hKeyLogQuery,
TCHAR* szQueryKeyNameBuffer,
TCHAR* szQueryNameBuffer )
{
DWORD dwStatus = ERROR_SUCCESS;
PLOG_QUERY_DATA pQuery = NULL;
pQuery = GetQueryData ( szQueryNameBuffer );
if ( NULL != pQuery ) {
BOOL bModified;
dwStatus = IsModified ( pQuery, &bModified );
if (dwStatus == ERROR_SUCCESS) {
if ( bModified ) {
dwStatus = ReconfigureQuery ( pQuery );
// LastModified and LastConfigured values are stored as GMT
GetSystemTimeAsFileTime ( (LPFILETIME)(&pQuery->llLastConfigured) );
}
}
} else {
// No query data block found. Create one and insert it into the list.
BOOL bStartQuery = FALSE;
LPTSTR szStringArray[2];
// Allocate a thread info block.
pQuery = G_ALLOC (sizeof(LOG_QUERY_DATA));
if (pQuery != NULL) {
// initialize the query data block
G_ZERO (pQuery, sizeof(LOG_QUERY_DATA));
pQuery->hKeyQuery = hKeyLogQuery;
lstrcpy (pQuery->szQueryName, szQueryNameBuffer);
lstrcpy (pQuery->szQueryKeyName, szQueryKeyNameBuffer);
// Determine whether to continue, based on whether start wait time
// is 0 or greater.
// The thread is reinitialized in the logging procedure.
// This pre-check avoids spurious thread creation.
dwStatus = LoadQueryConfig( pQuery );
if ( ERROR_SUCCESS != dwStatus ) {
// Event already logged.
bStartQuery = FALSE;
} else {
bStartQuery = ( NULL_INTERVAL_TICS != ComputeStartWaitTics ( pQuery, FALSE ) );
}
if ( bStartQuery ) {
if ( SLQ_TRACE_LOG == pQuery->dwLogType
|| SLQ_COUNTER_LOG == pQuery->dwLogType ) {
bStartQuery = ( ERROR_SUCCESS == ProcessLogFileFolder( pQuery, FALSE ) );
}
}
if ( bStartQuery ) {
LockQueryData();
if ( dwActiveSessionCount < dwMaxActiveSessionCount ) {
pQuery->hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if ( NULL != pQuery->hExitEvent ) {
pQuery->hReconfigEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if ( NULL != pQuery->hReconfigEvent ) {
// LastModified and LastConfigured values are stored as GMT
GetSystemTimeAsFileTime ( (LPFILETIME)(&pQuery->llLastConfigured) );
dwStatus = StartQuery( pQuery );
if ( ERROR_SUCCESS == dwStatus ) {
// add it to the list and continue
if (pFirstQuery == NULL) {
// then this is the first thread so add it
pQuery->next = NULL;
pFirstQuery = pQuery;
} else {
// insert this at the head of the list since
// that's the easiest and the order isn't
// really important
pQuery->next = pFirstQuery;
pFirstQuery = pQuery;
}
dwActiveSessionCount += 1;
SetEvent (hNewQueryEvent );
} else {
// Unable to start query.
// Event has already been logged.
FreeQueryData ( pQuery );
}
} else {
// Unable to create reconfig event.
dwStatus = GetLastError();
szStringArray[0] = szQueryNameBuffer;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_CREATE_RECONFIG_EVENT,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
FreeQueryData( pQuery );
}
} else {
// Unable to create exit event.
dwStatus = GetLastError();
szStringArray[0] = szQueryNameBuffer;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_CREATE_EXIT_EVENT,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
FreeQueryData( pQuery );
}
} else {
szStringArray[0] = szQueryNameBuffer;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_MAXIMUM_QUERY_LIMIT,
NULL,
1,
0,
szStringArray,
NULL);
dwStatus = HandleMaxQueriesExceeded ( pQuery );
FreeQueryData ( pQuery );
}
UnlockQueryData();
} else {
// Wait time is -1, or config load error.
FreeQueryData( pQuery );
}
} else {
// Memory allocation error.
dwStatus = GetLastError();
szStringArray[0] = szQueryNameBuffer;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_ALLOCATE_DATABLOCK,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
}
}
return dwStatus;
}
DWORD
DoLogCommandFile (
IN PLOG_QUERY_DATA pArg,
IN LPTSTR szLogFileName,
IN BOOL bStillRunning
)
{
DWORD dwStatus;
BOOL bStatus = FALSE;
const INT ciExtraChars = 3;
INT iBufLen = 0;
INT iStrLen = 0;
LPTSTR szCommandString = NULL;
LPTSTR szTempBuffer = NULL;
LONG lErrorMode;
LPWSTR szStringArray[3];
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS;
if ( NULL != pArg && NULL != szLogFileName ) {
if ( NULL != pArg->szCmdFileName ) {
dwStatus = pArg->dwCmdFileFailure;
if ( ERROR_SUCCESS == dwStatus ) {
iStrLen = lstrlen ( szLogFileName );
iBufLen = iStrLen + ciExtraChars + 1; // 1 is for NULL
szCommandString = (LPTSTR)G_ALLOC(iBufLen * sizeof(TCHAR));
iBufLen += lstrlen ( pArg->szCmdFileName ) + 1; // 1 is for space char,
// NULL already counted.
szTempBuffer = (LPTSTR)G_ALLOC(iBufLen * sizeof(TCHAR));
if ( NULL != szCommandString && NULL != szTempBuffer ) {
// build command line arguments
szCommandString[0] = _T('\"');
lstrcpy (&szCommandString[1], szLogFileName );
lstrcpy (&szCommandString[iStrLen+1], (LPCTSTR)(LPCTSTR)TEXT("\" "));
lstrcpy (&szCommandString[iStrLen+2],
(bStillRunning ? (LPCTSTR)(LPCTSTR)TEXT("1") : (LPCTSTR)TEXT("0")));
// initialize Startup Info block
memset (&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW ;
si.wShowWindow = SW_SHOWNOACTIVATE ;
//si.lpDesktop = L"WinSta0\\Default";
memset (&pi, 0, sizeof(pi));
// supress pop-ups in the detached process
lErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
lstrcpy (szTempBuffer, pArg->szCmdFileName) ;
// see if this is a CMD or a BAT file
// if it is then create a process with a console window, otherwise
// assume it's an executable file that will create it's own window
// or console if necessary
//
_tcslwr (szTempBuffer);
if ((_tcsstr(szTempBuffer, (LPCTSTR)TEXT(".bat")) != NULL)
|| (_tcsstr(szTempBuffer, (LPCTSTR)TEXT(".cmd")) != NULL))
{
dwCreationFlags |= CREATE_NEW_CONSOLE;
} else {
dwCreationFlags |= DETACHED_PROCESS;
}
// recopy the image name to the temp buffer since it was modified
// (i.e. lowercased) for the previous comparison.
lstrcpy (szTempBuffer, pArg->szCmdFileName) ;
iStrLen = lstrlen (szTempBuffer) ;
// now add on the alert text preceded with a space char
szTempBuffer [iStrLen] = TEXT(' ') ;
iStrLen++ ;
lstrcpy (&szTempBuffer[iStrLen], szCommandString) ;
if( pArg->hUserToken != NULL ){
bStatus = CreateProcessAsUser (
pArg->hUserToken,
NULL,
szTempBuffer,
NULL, NULL, FALSE,
dwCreationFlags,
NULL,
NULL,
&si,
&pi);
} else {
bStatus = CreateProcess (
NULL,
szTempBuffer,
NULL, NULL, FALSE,
dwCreationFlags,
NULL,
NULL,
&si,
&pi);
}
SetErrorMode(lErrorMode);
if (bStatus) {
dwStatus = ERROR_SUCCESS;
if ( NULL != pi.hThread && INVALID_HANDLE_VALUE != pi.hThread ) {
CloseHandle(pi.hThread);
pi.hThread = NULL;
}
if ( NULL != pi.hProcess && INVALID_HANDLE_VALUE != pi.hProcess ) {
CloseHandle(pi.hProcess);
pi.hProcess = NULL;
}
} else {
dwStatus = GetLastError();
}
} else {
dwStatus = ERROR_OUTOFMEMORY;
}
if ( ERROR_SUCCESS != dwStatus ) {
szStringArray[0] = szTempBuffer;
szStringArray[1] = pArg->szQueryName;
szStringArray[2] = FormatEventLogMessage(dwStatus);
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_LOG_CMD_FAIL,
NULL,
3,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus );
pArg->dwCmdFileFailure = dwStatus;
}
if (szCommandString != NULL) G_FREE(szCommandString);
if (szTempBuffer != NULL) G_FREE(szTempBuffer);
}
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
return dwStatus;
}
DWORD
GetQueryKeyName (
IN LPCTSTR szQueryName,
OUT LPTSTR szQueryKeyName,
IN DWORD dwQueryKeyNameLen )
{
DWORD dwStatus = ERROR_SUCCESS;
HKEY hKeyLogQueries = NULL;
HKEY hKeyThisLogQuery = NULL;
DWORD dwQueryIndex;
TCHAR szQueryNameBuffer[MAX_PATH+1];
DWORD dwQueryNameBufferSize;
TCHAR szQueryKeyNameBuffer[MAX_PATH+1];
TCHAR szQueryClassBuffer[MAX_PATH+1];
DWORD dwQueryClassBufferSize;
LPTSTR szCollectionName = NULL;
UINT uiCollectionNameLen = 0;
LPTSTR szStringArray[2];
assert ( 0 < lstrlen ( szQueryName ) );
if ( NULL != szQueryName
&& NULL != szQueryKeyName ) {
if ( 0 < lstrlen ( szQueryName )
&& 0 < dwQueryKeyNameLen ) {
// Note: This method does not reallocate buffer or return
// actual buffer size required.
memset ( szQueryKeyName, 0, dwQueryKeyNameLen * sizeof (TCHAR) );
dwStatus = OpenLogQueriesKey (
KEY_READ,
(PHKEY)&hKeyLogQueries);
if (dwStatus != ERROR_SUCCESS) {
// unable to read the log query information from the registry
dwStatus = GetLastError();
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
SMLOG_UNABLE_OPEN_LOG_QUERY,
NULL,
0,
0,
NULL,
NULL);
} else {
// Enumerate the queries in the registry.
dwQueryIndex = 0;
*szQueryNameBuffer = 0;
dwQueryNameBufferSize = MAX_PATH+1;
*szQueryClassBuffer = 0;
dwQueryClassBufferSize = MAX_PATH+1;
while ((dwStatus = RegEnumKeyEx (
hKeyLogQueries,
dwQueryIndex,
szQueryNameBuffer,
&dwQueryNameBufferSize,
NULL,
szQueryClassBuffer,
&dwQueryClassBufferSize,
NULL)) != ERROR_NO_MORE_ITEMS)
{
// open this key
dwStatus = RegOpenKeyEx (
hKeyLogQueries,
szQueryNameBuffer,
0L,
KEY_READ,
(PHKEY)&hKeyThisLogQuery);
if (dwStatus == ERROR_SUCCESS) {
if ( 0 == lstrcmpi ( szQueryNameBuffer, szQueryName ) ) {
if ( dwQueryKeyNameLen > (DWORD)lstrlen ( szQueryName ) ) {
lstrcpyn ( szQueryKeyName, szQueryName, min (MAX_PATH, lstrlen (szQueryName) + 1 ) );
break;
}
} else {
dwStatus = SmReadRegistryIndirectStringValue (
hKeyThisLogQuery,
L"Collection Name",
NULL,
&szCollectionName,
&uiCollectionNameLen );
if ( NULL != szCollectionName ) {
if ( 0 < lstrlen(szCollectionName) ) {
if ( 0 == lstrcmpi ( szCollectionName, szQueryName ) ) {
if ( dwQueryKeyNameLen > (DWORD)lstrlen ( szQueryNameBuffer ) ) {
lstrcpyn ( szQueryKeyName, szQueryNameBuffer, min (MAX_PATH, lstrlen (szQueryNameBuffer) + 1 ) );
break;
}
}
}
G_FREE ( szCollectionName );
szCollectionName = NULL;
uiCollectionNameLen = 0;
}
}
}
if ( NULL != hKeyThisLogQuery ) {
RegCloseKey ( hKeyThisLogQuery );
hKeyThisLogQuery = NULL;
}
// prepare for next loop
dwStatus = ERROR_SUCCESS;
dwQueryIndex++;
*szQueryNameBuffer = 0;
dwQueryNameBufferSize = MAX_PATH;
*szQueryClassBuffer = 0;
dwQueryClassBufferSize = MAX_PATH;
} // end enumeration of log queries
}
if ( ERROR_NO_MORE_ITEMS == dwStatus ) {
dwStatus = ERROR_SUCCESS;
}
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
if ( NULL != hKeyLogQueries ) {
RegCloseKey (hKeyLogQueries );
}
return dwStatus;
}
DWORD
Configure ( void )
{
DWORD dwStatus;
HKEY hKeyLogQueries = NULL;
HKEY hKeyThisLogQuery = NULL;
DWORD dwQueryIndex;
TCHAR szQueryNameBuffer[MAX_PATH+1];
DWORD dwQueryNameBufferSize;
TCHAR szQueryKeyNameBuffer[MAX_PATH+1];
TCHAR szQueryClassBuffer[MAX_PATH+1];
DWORD dwQueryClassBufferSize;
LPTSTR szCollectionName = NULL;
UINT uiCollectionNameLen = 0;
LPTSTR szStringArray[2];
__try {
// Open each query in the registry
dwStatus = OpenLogQueriesKey (
KEY_READ,
(PHKEY)&hKeyLogQueries);
if (dwStatus != ERROR_SUCCESS) {
if (dwStatus == ERROR_FILE_NOT_FOUND) {
// no logs nor alerts settings, bail out quietly
//
dwStatus = ERROR_SUCCESS;
}
else {
// unable to read the log query information from the registry
dwStatus = GetLastError();
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
SMLOG_UNABLE_OPEN_LOG_QUERY,
NULL,
0,
0,
NULL,
NULL);
}
} else {
// enumerate and restart or start the queries in the registry
dwQueryIndex = 0;
*szQueryNameBuffer = 0;
dwQueryNameBufferSize = MAX_PATH+1;
*szQueryClassBuffer = 0;
dwQueryClassBufferSize = MAX_PATH+1;
while ((dwStatus = RegEnumKeyEx (
hKeyLogQueries,
dwQueryIndex,
szQueryNameBuffer,
&dwQueryNameBufferSize,
NULL,
szQueryClassBuffer,
&dwQueryClassBufferSize,
NULL)) != ERROR_NO_MORE_ITEMS) {
// open this key
dwStatus = RegOpenKeyEx (
hKeyLogQueries,
szQueryNameBuffer,
0L,
KEY_READ | KEY_WRITE,
(PHKEY)&hKeyThisLogQuery);
if (dwStatus == ERROR_SUCCESS) {
// update the service status
ssSmLogStatus.dwCheckPoint++;
SetServiceStatus (hSmLogStatus, &ssSmLogStatus);
if ( 0 < lstrlen(szQueryNameBuffer) ) {
lstrcpyn (
szQueryKeyNameBuffer,
szQueryNameBuffer,
min(MAX_PATH, lstrlen(szQueryNameBuffer)+1 ) );
}
dwStatus = SmReadRegistryIndirectStringValue (
hKeyThisLogQuery,
L"Collection Name",
NULL,
&szCollectionName,
&uiCollectionNameLen );
if ( NULL != szCollectionName ) {
if ( 0 < lstrlen(szCollectionName) ) {
lstrcpyn (
szQueryNameBuffer,
szCollectionName,
min(MAX_PATH, lstrlen(szCollectionName)+1 ) );
}
G_FREE ( szCollectionName );
szCollectionName = NULL;
uiCollectionNameLen = 0;
}
dwStatus = ConfigureQuery (
hKeyThisLogQuery,
szQueryKeyNameBuffer,
szQueryNameBuffer );
// hKeyThisLogQuery is stored in the Query data structure.
} else {
szStringArray[0] = szQueryNameBuffer;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_LOG_QUERY,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
}
// prepare for next loop
dwStatus = ERROR_SUCCESS;
dwQueryIndex++;
*szQueryNameBuffer = 0;
dwQueryNameBufferSize = MAX_PATH;
*szQueryClassBuffer = 0;
dwQueryClassBufferSize = MAX_PATH;
} // end enumeration of log queries
}
if ( ERROR_NO_MORE_ITEMS == dwStatus ) {
dwStatus = ERROR_SUCCESS;
}
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
dwStatus = SMLOG_THREAD_FAILED;
}
if ( NULL != hKeyLogQueries ) {
RegCloseKey (hKeyLogQueries );
}
return dwStatus;
}
void SysmonLogServiceControlHandler(
IN DWORD dwControl
)
{
PLOG_QUERY_DATA pQuery;
DWORD dwStatus;
switch (dwControl) {
case SERVICE_CONTROL_SYNCHRONIZE:
EnterConfigure();
dwStatus = Configure ();
ExitConfigure();
if ( ERROR_SUCCESS == dwStatus )
break;
// If not successful, fall through to shutdown.
// Errors already logged.
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
// stop logging & close queries and files
// set stop event for all running thread
LockQueryData();
ssSmLogStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus (hSmLogStatus, &ssSmLogStatus);
pQuery = pFirstQuery;
while (pQuery != NULL) {
SetEvent (pQuery->hExitEvent);
pQuery = pQuery->next;
}
UnlockQueryData();
break;
case SERVICE_CONTROL_PAUSE:
// stop logging, close queries and files
// not supported, yet
break;
case SERVICE_CONTROL_CONTINUE:
// reload ration and restart logging
// not supported, yet
break;
case SERVICE_CONTROL_INTERROGATE:
// update current status
default:
// report to event log that an unrecognized control
// request was received.
SetServiceStatus (hSmLogStatus, &ssSmLogStatus);
}
}
void
SysmonLogServiceStart (
IN DWORD argc,
IN LPTSTR *argv
)
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwQueryIndex;
BOOL bInteractive = FALSE;
BOOL bLogQueriesKeyExists = TRUE;
if ((argc == 1) && (*argv[0] == 'I')) bInteractive = TRUE;
#if _DEBUG_OUTPUT
if ( NULL != hEventLog ) {
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_DEBUG_STARTING_SERVICE,
NULL,
0,
0,
NULL,
NULL);
}
#endif
if (!bInteractive) {
ssSmLogStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ssSmLogStatus.dwCurrentState = SERVICE_START_PENDING;
ssSmLogStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
// SERVICE_ACCEPT_PAUSE_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN;
ssSmLogStatus.dwWin32ExitCode = 0;
ssSmLogStatus.dwServiceSpecificExitCode = 0;
ssSmLogStatus.dwCheckPoint = 0;
ssSmLogStatus.dwWaitHint = 1000;
hSmLogStatus = RegisterServiceCtrlHandler (
(LPCTSTR)TEXT("SysmonLog"), SysmonLogServiceControlHandler);
if (hSmLogStatus == (SERVICE_STATUS_HANDLE)0) {
dwStatus = GetLastError();
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
SMLOG_UNABLE_REGISTER_HANDLER,
NULL,
0,
sizeof(DWORD),
NULL,
(LPVOID)&dwStatus);
// this is fatal so bail out
}
#if _DEBUG_OUTPUT
else {
if ( NULL != hEventLog ) {
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_DEBUG_HANDLER_REGISTERED,
NULL,
0,
0,
NULL,
NULL);
}
}
#endif
}
if ( ERROR_SUCCESS == dwStatus ) {
InitializeCriticalSection ( &QueryDataLock );
InitializeCriticalSection ( &ConfigurationLock );
#if _DEBUG_OUTPUT
ssSmLogStatus.dwCurrentState = SERVICE_RUNNING;
ssSmLogStatus.dwCheckPoint++;
SetServiceStatus (hSmLogStatus, &ssSmLogStatus);
#endif
dwStatus = ClearQueryRunStates();
#if _DEBUG_OUTPUT
if ( NULL != hEventLog ) {
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_DEBUG_CLEAR_RUN_STATES,
NULL,
sizeof(DWORD),
0,
NULL,
(LPVOID)&dwStatus);
}
#endif
// Continue even if query run state error, unless
// the Log Queries key is missing or not accessible.
if ( SMLOG_UNABLE_OPEN_LOG_QUERY == dwStatus ) {
bLogQueriesKeyExists = FALSE;
// Sleep long enough for event to be written to event log.
Sleep(500);
if (!bInteractive) {
ssSmLogStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
// Use status mask so that error matches the code in the application log.
ssSmLogStatus.dwServiceSpecificExitCode = (SMLOG_UNABLE_OPEN_LOG_QUERY & STATUS_MASK);
}
} else {
dwStatus = ERROR_SUCCESS;
// Continue on error.
LoadDefaultLogFileFolder();
// Ignore PDH errors. The only possible error is that the default
// data source has already been set for this process.
// Set the default for the service to DATA_SOURCE_REGISTRY
dwStatus = PdhSetDefaultRealTimeDataSource ( DATA_SOURCE_REGISTRY );
#if _DEBUG_OUTPUT
if ( NULL != hEventLog ) {
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_DEBUG_DEFAULT_FOLDER_LOADED,
NULL,
0,
0,
NULL,
NULL);
}
#endif
// Continue on error.
LoadPdhLogUpdateSuccess();
hNewQueryEvent = CreateEvent ( NULL, TRUE, FALSE, NULL );
if ( NULL == hNewQueryEvent ) {
// Unable to create new query configuration event.
dwStatus = GetLastError();
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_CREATE_CONFIG_EVENT,
NULL,
0,
sizeof(DWORD),
NULL,
(LPVOID)&dwStatus);
// this is fatal so bail out
if (!bInteractive) {
// Sleep long enough for event to be written to event log.
Sleep(500);
ssSmLogStatus.dwWin32ExitCode = dwStatus;
}
}
#if _DEBUG_OUTPUT
else {
if ( NULL != hEventLog ) {
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_DEBUG_CONFIG_EVENT_CREATED,
NULL,
0,
0,
NULL,
0);
}
}
#endif
if ( ( ERROR_SUCCESS == dwStatus ) && !bInteractive) {
// Thread synchronization mechanisms now created,
// so set status to Running.
ssSmLogStatus.dwCurrentState = SERVICE_RUNNING;
ssSmLogStatus.dwCheckPoint = 0;
SetServiceStatus (hSmLogStatus, &ssSmLogStatus);
}
#if _IMPLEMENT_WMI
if ( ERROR_SUCCESS == dwStatus ) {
// Disable 64-bit warning
#if _MSC_VER >= 1200
#pragma warning(push)
#endif
#pragma warning ( disable : 4152 )
dwStatus = WmiNotificationRegistration(
(const LPGUID) & TraceErrorGuid,
TRUE,
TraceNotificationCallback,
0,
NOTIFICATION_CALLBACK_DIRECT);
#if _MSC_VER >= 1200
#pragma warning(pop)
#endif
}
#endif
// Set up the queries and start threads.
if ( ERROR_SUCCESS == dwStatus && bLogQueriesKeyExists) {
EnterConfigure();
dwStatus = Configure ();
ExitConfigure();
}
if ( NULL == pFirstQuery ) {
// Nothing to do. Stop the service.
if (!bInteractive) {
ssSmLogStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus (hSmLogStatus, &ssSmLogStatus);
}
} else if ( ERROR_SUCCESS == dwStatus ) {
// Loop in WaitForMultipleObjects. When any
// query is signaled, deallocate that query data block
// and close its handles.
while ( TRUE ) {
BOOL bStatus;
LockQueryData();
// About to reconfigure the Wait array, so clear the event.
bStatus = ResetEvent ( hNewQueryEvent );
if ( NULL == pFirstQuery ) {
if (!bInteractive) {
ssSmLogStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus (hSmLogStatus, &ssSmLogStatus);
}
UnlockQueryData();
break;
} else {
DWORD dwIndex = 0;
DWORD dwWaitStatus;
PLOG_QUERY_DATA pQuery;
assert ( 0 < dwActiveSessionCount );
G_ZERO( arrSessionHandle, sizeof( HANDLE ) * ( dwActiveSessionCount + 1) );
// The first element is the global hNewQueryEvent to signal new sessions.
arrSessionHandle[dwIndex] = hNewQueryEvent;
dwIndex++;
for ( pQuery = pFirstQuery;
NULL != pQuery;
pQuery = pQuery->next ) {
assert ( NULL != pQuery->hThread );
if ( NULL != pQuery->hExitEvent && NULL != pQuery->hThread ) {
arrSessionHandle[dwIndex] = pQuery->hThread;
dwIndex++;
assert ( dwIndex <= dwActiveSessionCount + 1 );
}
}
UnlockQueryData();
// xxx handle error
dwWaitStatus = WaitForMultipleObjects (
dwIndex,
arrSessionHandle,
FALSE,
INFINITE );
// when here, either a new query has been started, or
// at least one logging thread or has terminated so the
// memory can be released.
dwQueryIndex = dwWaitStatus - WAIT_OBJECT_0;
// release the dynamic memory if the wait object is not the StartQuery event.
if ( 0 < dwQueryIndex && dwQueryIndex < dwIndex ) {
SetStoppedStatus( GetQueryDataPtr( arrSessionHandle[dwQueryIndex] ) );
RemoveAndFreeQueryData( arrSessionHandle[dwQueryIndex] );
}
}
} // End while
}
#if _IMPLEMENT_WMI
// Disable 64-bit warning
#if _MSC_VER >= 1200
#pragma warning(push)
#endif
#pragma warning ( disable : 4152 )
WmiNotificationRegistration(
(const LPGUID) & TraceErrorGuid,
FALSE,
TraceNotificationCallback,
0,
NOTIFICATION_CALLBACK_DIRECT);
#if _MSC_VER >= 1200
#pragma warning(pop)
#endif
#endif
if ( NULL != hNewQueryEvent ) {
CloseHandle ( hNewQueryEvent );
hNewQueryEvent = NULL;
}
}
DeleteCriticalSection ( &QueryDataLock );
DeleteCriticalSection ( &ConfigurationLock );
}
if (!bInteractive) {
// Update the service status
ssSmLogStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hSmLogStatus, &ssSmLogStatus);
}
if ( NULL != arrPdhDataCollectSuccess ) {
G_FREE ( arrPdhDataCollectSuccess );
arrPdhDataCollectSuccess = NULL;
iPdhDataCollectSuccessCount = 0;
}
if (hEventLog != NULL) {
DeregisterEventSource ( hEventLog );
hEventLog = NULL;
}
return;
}
int
__cdecl main (
int argc,
char *argv[])
/*++
main
Arguments
ReturnValue
0 (ERROR_SUCCESS) if command was processed
Non-Zero if command error was detected.
--*/
{
DWORD dwStatus = ERROR_SUCCESS;
BOOL bInteractive = FALSE;
SERVICE_TABLE_ENTRY DispatchTable[] = {
{(LPTSTR)TEXT("SysmonLog"), SysmonLogServiceStart },
{NULL, NULL }
};
hEventLog = RegisterEventSource (NULL, (LPCTSTR)TEXT("SysmonLog"));
#if _DEBUG_OUTPUT
if ( NULL != hEventLog ) {
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_DEBUG_EVENT_SOURCE_REGISTERED,
NULL,
0,
0,
NULL,
NULL);
}
#endif
hModule = (HINSTANCE) GetModuleHandle(NULL);
if (argc > 1) {
if ((argv[1][0] == '-') || (argv[1][0] == '/')) {
if ((argv[1][1] == 'i') || (argv[1][1] == 'I')) {
bInteractive = TRUE;
}
}
}
if (bInteractive) {
DWORD dwArgs = 1;
LPTSTR szArgs[1];
szArgs[0] = (LPTSTR)TEXT("I");
SysmonLogServiceStart (dwArgs, szArgs);
} else {
if (!StartServiceCtrlDispatcher (DispatchTable)) {
dwStatus = GetLastError();
// log failure to event log
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
SMLOG_UNABLE_START_DISPATCHER,
NULL,
0,
sizeof(DWORD),
NULL,
(LPVOID)&dwStatus);
}
#if _DEBUG_OUTPUT
else {
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_DEBUG_SERVICE_CTRL_DISP_STARTED,
NULL,
0,
0,
NULL,
NULL);
}
#endif
}
return dwStatus;
}