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

2797 lines
106 KiB
C
Raw 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:
logthred.c
Abstract:
Performance Logs and Alerts log/scan thread functions.
--*/
#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)
#include <assert.h>
// For Trace *** - these are only necessary because of union query data struct.
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <wtypes.h>
#include <float.h>
#include <limits.h>
#if _IMPLEMENT_WMI
#include <wmistr.h>
#include <evntrace.h>
#endif
#include <lmcons.h>
#include <lmmsg.h> // for net message function
#include <stdio.h>
#include <tchar.h>
#include <pdh.h>
#include <pdhp.h>
#include <pdhmsg.h>
#include "smlogsvc.h"
#include "smlogmsg.h"
#define SECONDS_IN_DAY ((LONGLONG)(86400))
#define LOG_EVENT_ON_ERROR ((BOOL)(1))
DWORD
ValidateCommandFilePath (
IN PLOG_QUERY_DATA pArg )
{
DWORD dwStatus = ERROR_SUCCESS;
if ( 0 != lstrlen ( pArg->szCmdFileName ) ) {
HANDLE hOpenFile;
LONG lErrorMode;
lErrorMode = SetErrorMode ( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX );
hOpenFile = CreateFile (
pArg->szCmdFileName,
GENERIC_READ,
0, // Not shared
NULL, // Security attributes
OPEN_EXISTING, //
FILE_ATTRIBUTE_NORMAL,
NULL );
if ( ( NULL == hOpenFile )
|| INVALID_HANDLE_VALUE == hOpenFile ) {
LPWSTR szStringArray[3];
DWORD dwStatus = GetLastError();
szStringArray[0] = pArg->szCmdFileName;
szStringArray[1] = pArg->szQueryName;
szStringArray[2] = FormatEventLogMessage(dwStatus);
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_CMD_FILE_INVALID,
NULL,
3,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus );
pArg->dwCmdFileFailure = dwStatus;
} else {
CloseHandle(hOpenFile);
}
SetErrorMode ( lErrorMode );
}
return dwStatus;
}
DWORD
AddCounterToCounterLog (
IN PLOG_QUERY_DATA pArg,
IN LPWSTR pszThisPath,
IN HANDLE hQuery,
IN BOOL bLogErrorEvent,
IN OUT DWORD* pdwCounterCount )
{
LPWSTR szStringArray[3];
DWORD dwStatus = ERROR_SUCCESS;
HCOUNTER hThisCounter = NULL;
PDH_STATUS pdhStatus;
PLOG_COUNTER_INFO pCtrInfo = NULL;
dwStatus = pdhStatus = PdhAdd009Counter (
hQuery,
pszThisPath,
(* pdwCounterCount),
&hThisCounter);
if (dwStatus != ERROR_SUCCESS) {
dwStatus = pdhStatus = PdhAddCounter (
hQuery,
pszThisPath,
(* pdwCounterCount),
&hThisCounter);
}
if ( !IsErrorSeverity(pdhStatus) ) {
if ( IsWarningSeverity(pdhStatus) ) {
// Write event log warning message
szStringArray[0] = pszThisPath;
szStringArray[1] = pArg->szQueryName;
szStringArray[2] = FormatEventLogMessage(pdhStatus);
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_ADD_COUNTER_WARNING,
NULL,
3,
sizeof(DWORD),
szStringArray,
(LPVOID)&pdhStatus);
}
// then add this handle to the list
(*pdwCounterCount)++;
pCtrInfo = G_ALLOC (sizeof (LOG_COUNTER_INFO));
if (pCtrInfo != NULL) {
// insert at front of list since the order isn't
// important and this is simpler than walking the
// list each time.
pCtrInfo->hCounter = hThisCounter;
pCtrInfo->next = pArg->pFirstCounter;
pArg->pFirstCounter = pCtrInfo;
pCtrInfo = NULL;
} else {
dwStatus = ERROR_OUTOFMEMORY;
}
} else {
// For LogByObject, the call is retried with expanded counter if
// the first try fails, so don't log error event the first time.
if ( bLogErrorEvent ) {
// unable to add the current counter so write event log message
szStringArray[0] = pszThisPath;
szStringArray[1] = pArg->szQueryName;
szStringArray[2] = FormatEventLogMessage(pdhStatus);
if ( PDH_ACCESS_DENIED == pdhStatus ) {
ReportEvent (
hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_ACCESS_COUNTER,
NULL,
2,
0,
szStringArray,
NULL);
} else {
ReportEvent (
hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_ADD_COUNTER,
NULL,
3,
sizeof(DWORD),
szStringArray,
(LPVOID)&pdhStatus);
}
}
}
return dwStatus;
}
BOOL
IsPdhDataCollectSuccess ( PDH_STATUS pdhStatus )
{
BOOL bSuccess = FALSE;
if ( ERROR_SUCCESS == pdhStatus
|| PDH_INVALID_DATA == pdhStatus ) {
bSuccess = TRUE;
} else if ( 0 < iPdhDataCollectSuccessCount ) {
INT iIndex;
for ( iIndex = 0; iIndex < iPdhDataCollectSuccessCount; iIndex++ ) {
if ( pdhStatus == (PDH_STATUS)arrPdhDataCollectSuccess[iIndex] ) {
bSuccess = TRUE;
break;
}
}
}
return bSuccess;
}
void
ComputeSessionTics(
IN PLOG_QUERY_DATA pArg,
IN OUT LONGLONG* pllWaitTics
)
{
LONGLONG llLocalTime;
// Compute total session time based on Stop modes
// and values.
// -1 (NULL_INTERVAL_TICS) signals no session time limit. This is true for
// Stop mode SLQ_AUTO_MODE_NONE and SLQ_AUTO_MODE_SIZE.
//
// 0 signals that the Stop time is past, so exit immediately.
//
// Assume that session is starting, so Start mode isn't relevant.
if ( NULL != pArg && NULL != pllWaitTics ) {
*pllWaitTics = NULL_INTERVAL_TICS;
if ( SLQ_AUTO_MODE_AFTER == pArg->stiCurrentStop.dwAutoMode
|| SLQ_AUTO_MODE_AT == pArg->stiCurrentStop.dwAutoMode ) {
GetLocalFileTime (&llLocalTime);
if ( SLQ_AUTO_MODE_AT == pArg->stiCurrentStop.dwAutoMode ) {
if ( pArg->stiCurrentStop.llDateTime > llLocalTime ) {
*pllWaitTics = pArg->stiCurrentStop.llDateTime - llLocalTime;
} else {
// Session length = 0. Exit immediately.
*pllWaitTics = ((LONGLONG)(0));
}
} else if ( SLQ_AUTO_MODE_AFTER == pArg->stiCurrentStop.dwAutoMode ) {
TimeInfoToTics( &pArg->stiCurrentStop, pllWaitTics );
}
}
} else {
assert ( FALSE );
}
// Todo: Report errors
return;
}
void
ComputeNewFileTics(
IN PLOG_QUERY_DATA pArg,
IN OUT LONGLONG* pllWaitTics
)
{
LONGLONG llLocalTime;
// Compute time until next file creation based on Create New File modes
// and values.
// -1 (NULL_INTERVAL_TICS) signals no time limit. This is true for
// mode SLQ_AUTO_MODE_NONE and SLQ_AUTO_MODE_SIZE.
//
// 0 signals that the time is past, so exit immediately.
//
// Assume that session is starting, so Start mode isn't relevant.
if ( NULL != pArg && NULL != pllWaitTics ) {
*pllWaitTics = NULL_INTERVAL_TICS;
if ( SLQ_AUTO_MODE_AFTER == pArg->stiCreateNewFile.dwAutoMode ) {
GetLocalFileTime (&llLocalTime);
if ( SLQ_AUTO_MODE_AFTER == pArg->stiCreateNewFile.dwAutoMode ) {
TimeInfoToTics( &pArg->stiCreateNewFile, pllWaitTics );
assert ( (LONGLONG)(0) != *pllWaitTics );
} else if ( SLQ_AUTO_MODE_AT == pArg->stiCreateNewFile.dwAutoMode ) {
assert ( FALSE );
*pllWaitTics = (LONGLONG)(0);
}
}
} else {
assert ( FALSE );
}
// Todo: Report errors
return;
}
void
ComputeSampleCount(
IN PLOG_QUERY_DATA pArg,
IN BOOL bSessionCount,
OUT LONGLONG* pllSampleCount
)
{
// Compute sample count based on Stop or CreateNewFile modes
// and values. Account for the first sample in the log.
//
// 0 signals no sample limit in the file. This is true for
// Stop modes SLQ_AUTO_MODE_NONE and SLQ_AUTO_MODE_SIZE.
//
// -1 signals that the Stop time is past.
// Assume that sampling is starting, so Start mode isn't relevant.
LONGLONG llLocalSampleCount = NULL_INTERVAL_TICS;
assert ( NULL != pllSampleCount );
if ( NULL != pllSampleCount ) {
*pllSampleCount = (LONGLONG)(-1);
if ( bSessionCount ) {
ComputeSessionTics ( pArg, &llLocalSampleCount );
} else {
ComputeNewFileTics ( pArg, &llLocalSampleCount );
}
if ( NULL_INTERVAL_TICS == llLocalSampleCount ) {
// No session/sample limit
*pllSampleCount = (LONGLONG)(0);
} else if ( (LONGLONG)(0) == llLocalSampleCount ){
// Stop time is past
*pllSampleCount = INFINITE_TICS;
} else {
*pllSampleCount = llLocalSampleCount
/ (pArg->dwMillisecondSampleInterval * FILETIME_TICS_PER_MILLISECOND);
*pllSampleCount += 1; // add in the "zero-th" sample
}
}
return;
}
BOOL
ProcessRepeatOption (
IN OUT PLOG_QUERY_DATA pArg,
OUT LARGE_INTEGER* pliStartDelayTics )
{
BOOL bRepeat = TRUE;
if ( NULL != pliStartDelayTics ) {
// If restart not enabled, then exit.
if ( SLQ_AUTO_MODE_NONE == pArg->stiRepeat.dwAutoMode ) {
pliStartDelayTics->QuadPart = NULL_INTERVAL_TICS;
bRepeat = FALSE;
} else {
// For SLQ_AUTO_MODE_AFTER, the only value currently supported is 0.
pliStartDelayTics->QuadPart = (LONGLONG)0;
// For SLQ_AUTO_MODE_CALENDAR, add n*24 hours to the original start time.
// If Stop mode is SLQ_AUTO_MODE_AT, add n*24 hours to stop time.
if ( SLQ_AUTO_MODE_CALENDAR == pArg->stiRepeat.dwAutoMode ) {
// Delay of NULL_INTERVAL signals exit immediately.
// Todo: This should not occur. Report Event, assert.
pliStartDelayTics->QuadPart = ComputeStartWaitTics ( pArg, TRUE );
if ( NULL_INTERVAL_TICS == pliStartDelayTics->QuadPart ) {
bRepeat = FALSE;
} else {
pArg->dwCurrentState = SLQ_QUERY_START_PENDING;
WriteRegistryDwordValue (
pArg->hKeyQuery,
(LPCWSTR)L"Current State",
&pArg->dwCurrentState,
REG_DWORD );
// Todo: Warning event on failure.
}
} // else for SLQ_AUTO_MODE_AFTER, repeat immediately
}
} else {
bRepeat = FALSE;
assert ( FALSE );
}
return bRepeat;
}
void
SetPdhOpenOptions (
IN PLOG_QUERY_DATA pArg,
OUT DWORD* pdwAccess,
OUT DWORD* pdwLogFileType )
{
// get file type
switch ( pArg->dwLogFileType ) {
case SLF_TSV_FILE:
*pdwLogFileType = PDH_LOG_TYPE_TSV;
break;
case SLF_BIN_FILE:
case SLF_BIN_CIRC_FILE:
*pdwLogFileType = PDH_LOG_TYPE_BINARY;
break;
case SLF_SQL_LOG:
*pdwLogFileType = PDH_LOG_TYPE_SQL;
break;
case SLF_CSV_FILE:
default:
*pdwLogFileType = PDH_LOG_TYPE_CSV;
break;
}
*pdwAccess = PDH_LOG_WRITE_ACCESS |
PDH_LOG_CREATE_ALWAYS;
if (SLF_BIN_CIRC_FILE == pArg->dwLogFileType)
*pdwAccess |= PDH_LOG_OPT_CIRCULAR;
if ( ( PDH_LOG_TYPE_BINARY != *pdwLogFileType )
&& (NULL != pArg->szLogFileComment ) )
*pdwAccess |= PDH_LOG_OPT_USER_STRING;
// NOTE: For all types except sequential binary,
// the append mode is determined by the file type.
// All Sql logs are APPEND
// All text logs are OVERWRITE
if ( (pArg->dwAppendMode)
&& (* pdwLogFileType == PDH_LOG_TYPE_BINARY) ) {
* pdwAccess |= PDH_LOG_OPT_APPEND;
}
}
DWORD
StartLogQuery (
IN PLOG_QUERY_DATA pArg
)
{
HKEY hKeyLogQuery;
SLQ_TIME_INFO slqTime;
DWORD dwStatus;
SC_HANDLE hSC = NULL;
SC_HANDLE hService = NULL;
SERVICE_STATUS ssData;
WCHAR szQueryKeyNameBuf[MAX_PATH];
WCHAR szLogPath[2*MAX_PATH];
DWORD dwCurrentState;
DWORD dwValue;
DWORD dwDefault;
SYSTEMTIME st;
LONGLONG llTime;
LONGLONG llModifiedTime;
// open registry key to the desired service
dwStatus = GetQueryKeyName (
pArg->szPerfLogName,
szQueryKeyNameBuf,
MAX_PATH );
if ( ERROR_SUCCESS == dwStatus && 0 < lstrlen (szQueryKeyNameBuf) ) {
lstrcpyW (szLogPath, (LPCWSTR)L"SYSTEM\\CurrentControlSet\\Services\\SysmonLog\\Log Queries");
lstrcatW (szLogPath, (LPCWSTR)L"\\");
lstrcatW (szLogPath, szQueryKeyNameBuf);
dwStatus = RegOpenKeyEx (
(HKEY)HKEY_LOCAL_MACHINE,
szLogPath,
0L,
KEY_READ | KEY_WRITE,
(PHKEY)&hKeyLogQuery);
if (dwStatus == ERROR_SUCCESS) {
// if current state is running, then skip the rest
dwDefault = SLQ_QUERY_STOPPED;
dwStatus = ReadRegistryDwordValue (
hKeyLogQuery,
pArg->szPerfLogName,
(LPCWSTR)L"Current State",
&dwDefault,
&dwCurrentState);
if (dwCurrentState == SLQ_QUERY_STOPPED) {
// update the start time to MIN_TIME_VALUE
GetLocalTime(&st);
SystemTimeToFileTime (&st, (FILETIME *)&llTime);
memset (&slqTime, 0, sizeof(slqTime));
slqTime.wTimeType = SLQ_TT_TTYPE_START;
slqTime.wDataType = SLQ_TT_DTYPE_DATETIME;
slqTime.dwAutoMode = SLQ_AUTO_MODE_NONE;
slqTime.llDateTime = MIN_TIME_VALUE;
dwStatus = WriteRegistrySlqTime (
hKeyLogQuery,
(LPCWSTR)L"Start",
&slqTime);
// If stop time mode set to manual, or StopAt with time before Now,
// set the mode to Manual, value to MAX_TIME_VALUE
memset (&slqTime, 0, sizeof(slqTime));
slqTime.wTimeType = SLQ_TT_TTYPE_STOP;
slqTime.wDataType = SLQ_TT_DTYPE_DATETIME;
slqTime.dwAutoMode = SLQ_AUTO_MODE_NONE;
slqTime.llDateTime = MAX_TIME_VALUE;
dwStatus = ReadRegistrySlqTime (
hKeyLogQuery,
pArg->szPerfLogName,
(LPCWSTR)L"Stop",
&slqTime,
&slqTime);
if ( SLQ_AUTO_MODE_NONE == slqTime.dwAutoMode
|| ( SLQ_AUTO_MODE_AT == slqTime.dwAutoMode
&& llTime >= slqTime.llDateTime ) ) {
slqTime.wTimeType = SLQ_TT_TTYPE_STOP;
slqTime.wDataType = SLQ_TT_DTYPE_DATETIME;
slqTime.dwAutoMode = SLQ_AUTO_MODE_NONE;
slqTime.llDateTime = MAX_TIME_VALUE;
dwStatus = WriteRegistrySlqTime (
hKeyLogQuery,
(LPCWSTR)L"Stop",
&slqTime);
}
// Set state to start pending.
if (dwStatus == ERROR_SUCCESS) {
dwValue = SLQ_QUERY_START_PENDING;
dwStatus = WriteRegistryDwordValue (
hKeyLogQuery,
(LPCWSTR)L"Current State",
&dwValue,
REG_DWORD);
}
// update the modified time to indicate a change has occurred
memset (&slqTime, 0, sizeof(slqTime));
// LastModified and LastConfigured values are stored as GMT
GetSystemTimeAsFileTime ( (LPFILETIME)(&llModifiedTime) );
slqTime.wTimeType = SLQ_TT_TTYPE_LAST_MODIFIED;
slqTime.wDataType = SLQ_TT_DTYPE_DATETIME;
slqTime.dwAutoMode = SLQ_AUTO_MODE_NONE;
slqTime.llDateTime = llModifiedTime;
dwStatus = WriteRegistrySlqTime (
hKeyLogQuery,
(LPCWSTR)L"Last Modified",
&slqTime);
if (dwStatus == ERROR_SUCCESS) {
hSC = OpenSCManager ( NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSC != NULL) {
// ping the service controller to rescan the entries
hService = OpenServiceW (
hSC,
(LPCWSTR)L"SysmonLog",
SERVICE_USER_DEFINED_CONTROL | SERVICE_START );
if (hService != NULL) {
ControlService (
hService,
SERVICE_CONTROL_SYNCHRONIZE,
&ssData);
CloseServiceHandle (hService);
} else {
// unable to open log service
dwStatus = GetLastError();
}
CloseServiceHandle (hSC);
} else {
// unable to open service controller
dwStatus = GetLastError();
}
} else {
// unable to set the time
}
RegCloseKey (hKeyLogQuery);
if ( ( ERROR_SUCCESS != dwStatus )
&& ( 1 != pArg->dwAlertLogFailureReported ) ) {
LPWSTR szStringArray[2];
szStringArray[0] = pArg->szPerfLogName;
szStringArray[1] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_START_ALERT_LOG,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus );
pArg->dwAlertLogFailureReported = 1;
}
} else {
// it's probably already running so don't bother with it
dwStatus = ERROR_SUCCESS;
}
} else {
dwStatus = SMLOG_UNABLE_READ_ALERT_LOG;
if ( 1 != pArg->dwAlertLogFailureReported ) {
LPWSTR szStringArray[2];
szStringArray[0] = pArg->szPerfLogName;
szStringArray[1] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_ALERT_LOG,
NULL,
2,
0,
szStringArray,
NULL );
pArg->dwAlertLogFailureReported = 1;
}
}
} else {
dwStatus = SMLOG_UNABLE_READ_ALERT_LOG;
if ( 1 != pArg->dwAlertLogFailureReported ) {
LPWSTR szStringArray[2];
szStringArray[0] = pArg->szPerfLogName;
szStringArray[1] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_READ_ALERT_LOG,
NULL,
2,
0,
szStringArray,
NULL );
pArg->dwAlertLogFailureReported = 1;
}
}
return dwStatus;
}
DWORD
DoAlertCommandFile (
IN PLOG_QUERY_DATA pArg,
IN PALERT_COUNTER_INFO pAlertCI,
IN LPCWSTR szTimeStamp,
IN LPCWSTR szMeasuredValue,
IN LPCWSTR szOverUnder,
IN LPCWSTR szLimitValue
)
{
const INT ciMaxDelimPerArg = 3;
DWORD dwStatus = ERROR_SUCCESS;
BOOL bStatus = FALSE;
LPTSTR szCommandString = NULL;
INT iBufLen = 0;
LPTSTR szTempBuffer = NULL;
LONG lErrorMode;
DWORD iStrLen;
DWORD dwCmdFlags;
BOOL bSingleArg = FALSE;
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS;
LPWSTR szDelim1;
LPWSTR szDelim2;
BOOL bFirstArgDone = FALSE;
if ( NULL != pArg
&& NULL != pAlertCI ) {
if ( NULL != pArg->szCmdFileName ) {
dwStatus = pArg->dwCmdFileFailure;
if ( ERROR_SUCCESS == dwStatus ) {
// See if any of the argument flags are set.
dwCmdFlags = pArg->dwAlertActionFlags & ALRT_CMD_LINE_MASK;
if ( 0 != dwCmdFlags ) {
// Allocate space for all arguments
if ( NULL != pArg->szQueryName ) {
iBufLen += lstrlen ( pArg->szQueryName ) + ciMaxDelimPerArg;
}
if ( NULL != szTimeStamp ) {
iBufLen += lstrlen ( szTimeStamp ) + ciMaxDelimPerArg;
}
if ( NULL != pAlertCI->pAlertInfo->szCounterPath) {
iBufLen += lstrlen ( pAlertCI->pAlertInfo->szCounterPath ) + ciMaxDelimPerArg;
}
if ( NULL != szMeasuredValue ) {
iBufLen += lstrlen ( szMeasuredValue ) + ciMaxDelimPerArg;
}
if ( NULL != szOverUnder ) {
iBufLen += lstrlen ( szOverUnder ) + ciMaxDelimPerArg;
}
if ( NULL != szLimitValue ) {
iBufLen += lstrlen ( szLimitValue ) + ciMaxDelimPerArg;
}
if ( NULL != pArg->szUserText ) {
iBufLen += lstrlen ( pArg->szUserText ) + ciMaxDelimPerArg;
}
iBufLen+= 2; // 1 for possible leading ", 1 for NULL.
szCommandString = (LPWSTR)G_ALLOC(iBufLen * sizeof(TCHAR));
if ( NULL != szCommandString ) {
szCommandString[0] = _T('\0');
// build command line arguments
if ((pArg->dwAlertActionFlags & ALRT_CMD_LINE_SINGLE) != 0) {
bSingleArg = TRUE;
szDelim1 = (LPWSTR)L",";
szDelim2 = (LPWSTR)L"\0";
} else {
// multiple arguments enclosed by double quotes and
// separated by a space
szDelim1 = (LPWSTR)L" \"";
szDelim2 = (LPWSTR)L"\"";
}
if (pArg->dwAlertActionFlags & ALRT_CMD_LINE_A_NAME ) {
if ( NULL != pArg->szQueryName ) {
if (bFirstArgDone) {
lstrcatW(szCommandString, szDelim1); // add leading delimiter
} else {
lstrcatW(szCommandString, (LPCWSTR)L"\""); // add leading quote
bFirstArgDone = TRUE;
}
lstrcatW(szCommandString, pArg->szQueryName);
lstrcatW(szCommandString, szDelim2);
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
}
if ( ERROR_SUCCESS == dwStatus
&& ( pArg->dwAlertActionFlags & ALRT_CMD_LINE_D_TIME ) )
{
if ( NULL != szTimeStamp ) {
if (bFirstArgDone) {
lstrcatW(szCommandString, szDelim1); // add leading delimiter
} else {
lstrcatW(szCommandString, (LPCWSTR)L"\""); // add leading quote
bFirstArgDone = TRUE;
}
lstrcatW(szCommandString, szTimeStamp);
lstrcatW(szCommandString, szDelim2);
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
}
if ( ERROR_SUCCESS == dwStatus
&& ( pArg->dwAlertActionFlags & ALRT_CMD_LINE_C_NAME ) )
{
if ( NULL != pAlertCI->pAlertInfo->szCounterPath ) {
if (bFirstArgDone) {
lstrcatW(szCommandString, szDelim1); // add leading delimiter
} else {
lstrcatW(szCommandString, (LPCWSTR)L"\""); // add leading quote
bFirstArgDone = TRUE;
}
lstrcatW(szCommandString, pAlertCI->pAlertInfo->szCounterPath);
lstrcatW(szCommandString, szDelim2);
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
}
if ( ERROR_SUCCESS == dwStatus
&& ( pArg->dwAlertActionFlags & ALRT_CMD_LINE_M_VAL ) )
{
if ( NULL != szMeasuredValue ) {
if (bFirstArgDone) {
lstrcatW(szCommandString, szDelim1); // add leading delimiter
} else {
lstrcatW(szCommandString, (LPCWSTR)L"\""); // add leading quote
bFirstArgDone = TRUE;
}
lstrcatW(szCommandString, szMeasuredValue);
lstrcatW(szCommandString, szDelim2);
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
}
if ( ERROR_SUCCESS == dwStatus
&& ( pArg->dwAlertActionFlags & ALRT_CMD_LINE_L_VAL ) )
{
if ( NULL != szOverUnder && NULL != szLimitValue ) {
if (bFirstArgDone) {
lstrcatW(szCommandString, szDelim1); // add leading delimiter
} else {
lstrcatW(szCommandString, (LPCWSTR)L"\""); // add leading quote
bFirstArgDone = TRUE;
}
lstrcatW(szCommandString, szOverUnder);
lstrcatW(szCommandString, (LPCWSTR)L" ");
lstrcatW(szCommandString, szLimitValue);
lstrcatW(szCommandString, szDelim2);
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
}
if ( ERROR_SUCCESS == dwStatus
&& ( pArg->dwAlertActionFlags & ALRT_CMD_LINE_U_TEXT ) )
{
if ( NULL != pArg->szUserText ) {
if (bFirstArgDone) {
lstrcatW(szCommandString, szDelim1); // add leading delimiter
} else {
lstrcatW(szCommandString, (LPCWSTR)L"\""); // add leading quote
bFirstArgDone = TRUE;
}
lstrcatW(szCommandString, pArg->szUserText);
lstrcatW(szCommandString, szDelim2);
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
}
if (bFirstArgDone && bSingleArg) {
// add closing quote if there's at least 1 arg in the command line
lstrcatW(szCommandString, (LPCWSTR)L"\"");
}
} else {
dwStatus = ERROR_OUTOFMEMORY;
}
if ( ERROR_SUCCESS == dwStatus )
{
iBufLen = lstrlen( pArg->szCmdFileName ) + 1; // 1 for NULL
if ( NULL != szCommandString ) {
iBufLen += lstrlen ( szCommandString ) + 1; // 1 for space char
}
szTempBuffer = (LPWSTR)G_ALLOC(iBufLen * sizeof(TCHAR));
}
if ( NULL != szTempBuffer ) {
// build command line arguments
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) ;
if ( NULL != szCommandString ) {
// now add on the alert text preceded with a space char
iStrLen = lstrlen (szTempBuffer) ;
szTempBuffer [iStrLen] = TEXT(' ') ;
iStrLen++ ;
lstrcpy (&szTempBuffer[iStrLen], szCommandString) ;
}
// 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 inf the detached process
lErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
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 (szCommandString != NULL) G_FREE(szCommandString);
if (szTempBuffer != NULL) G_FREE(szTempBuffer);
}
}
if ( ERROR_SUCCESS != dwStatus ) {
LPWSTR szStringArray[2];
szStringArray[0] = szTempBuffer;
szStringArray[1] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_ALERT_CMD_FAIL,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus );
pArg->dwCmdFileFailure = dwStatus;
}
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
} else {
dwStatus = ERROR_INVALID_PARAMETER;
}
return dwStatus;
}
BOOL
ExamineAlertValues (
IN PLOG_QUERY_DATA pArg
)
{
PALERT_COUNTER_INFO pAlertCI;
PDH_STATUS pdhStatus;
DWORD dwType;
PDH_FMT_COUNTERVALUE pdhCurrentValue;
BOOL bDoAlertAction;
// for each counter in query, compare it's formatted
// value against the alert value and do the desired operation
// if the alert condition is exceeded.
for (pAlertCI = (PALERT_COUNTER_INFO)pArg->pFirstCounter;
pAlertCI != NULL;
pAlertCI = pAlertCI->next) {
bDoAlertAction = FALSE;
// get formatted counter value
pdhStatus = PdhGetFormattedCounterValue (
pAlertCI->hCounter,
PDH_FMT_DOUBLE | PDH_FMT_NOCAP100,
&dwType,
&pdhCurrentValue);
if ((pdhStatus == ERROR_SUCCESS) &&
((pdhCurrentValue.CStatus == PDH_CSTATUS_VALID_DATA) ||
(pdhCurrentValue.CStatus == PDH_CSTATUS_NEW_DATA))) {
// then the value was good so compare it
if ((pAlertCI->pAlertInfo->dwFlags & AIBF_OVER) == AIBF_OVER) {
// test for value > limit
if (pdhCurrentValue.doubleValue > pAlertCI->pAlertInfo->dLimit) {
bDoAlertAction = TRUE;
}
} else {
// test for value < limit
if (pdhCurrentValue.doubleValue < pAlertCI->pAlertInfo->dLimit) {
bDoAlertAction = TRUE;
}
}
}
if (bDoAlertAction) {
WCHAR szValueString[32];
WCHAR szLimitString[32];
WCHAR szOverUnderString[64];
WCHAR szTimeStampFmt[64];
WCHAR szTimeStamp[48];
DWORD dwFmtStringFlags;
DWORD dwBufLen;
SYSTEMTIME st;
// build arguments used by event log and net messsage if either
// option is enabled
dwFmtStringFlags = ALRT_ACTION_LOG_EVENT | ALRT_ACTION_SEND_MSG | ALRT_ACTION_EXEC_CMD;
if ((pArg->dwAlertActionFlags & dwFmtStringFlags) != 0) {
INT nResId;
// report event to event log
// format message string elements
_stprintf (szValueString, (LPCWSTR)L"%.*g", DBL_DIG, pdhCurrentValue.doubleValue);
_stprintf (szLimitString, (LPCWSTR)L"%.*g", DBL_DIG, pAlertCI->pAlertInfo->dLimit);
nResId = pAlertCI->pAlertInfo->dwFlags & AIBF_OVER ? IDS_OVER : IDS_UNDER;
LoadString (hModule,
nResId,
szOverUnderString,
(sizeof(szOverUnderString) / sizeof(szOverUnderString[0])));
// get timestampformat string
LoadString (hModule,
IDS_ALERT_TIMESTAMP_FMT,
szTimeStampFmt,
(sizeof(szTimeStampFmt) / sizeof(szTimeStampFmt[0])));
// message format string expects the following args:
// Timestamp
// Counter path string
// measured value
// over/under
// limit value
GetLocalTime (&st);
dwBufLen = swprintf (
szTimeStamp,
szTimeStampFmt,
st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond);
assert (dwBufLen < (sizeof(szTimeStamp) / sizeof(szTimeStamp[0])));
}
// do action(s) as defined in flags
if ((pArg->dwAlertActionFlags & ALRT_ACTION_LOG_EVENT) == ALRT_ACTION_LOG_EVENT) {
LPWSTR szStringArray[4];
szStringArray[0] = pAlertCI->pAlertInfo->szCounterPath;
szStringArray[1] = szValueString;
szStringArray[2] = szOverUnderString;
szStringArray[3] = szLimitString;
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_ALERT_LIMIT_CROSSED,
NULL,
4,
0,
szStringArray,
NULL);
}
if ((pArg->dwAlertActionFlags & ALRT_ACTION_SEND_MSG) == ALRT_ACTION_SEND_MSG) {
if (pArg->szNetName != NULL) {
DWORD dwStatus = ERROR_SUCCESS;
WCHAR szMessageFormat[MAX_PATH];
WCHAR szMessageText[MAX_PATH * 2];
DWORD dwBufLen;
// get message format string
LoadString (hModule,
IDS_ALERT_MSG_FMT,
szMessageFormat,
(sizeof(szMessageFormat) / sizeof(szMessageFormat[0])));
// message format string expects the following args:
// Timestamp
// Counter path string
// measured value
// over/under
// limit value
dwBufLen = swprintf (szMessageText, szMessageFormat,
szTimeStamp,
pAlertCI->pAlertInfo->szCounterPath,
szValueString,
szOverUnderString,
szLimitString);
assert (dwBufLen < (sizeof(szMessageText) / sizeof(szMessageText[0])));
dwBufLen += 1;
dwBufLen *= sizeof(WCHAR);
// send network message to specified computer
dwStatus = NetMessageBufferSend(
NULL,
pArg->szNetName,
NULL,
(LPBYTE)szMessageText,
dwBufLen);
if ( ( ERROR_SUCCESS != dwStatus )
&& ( 1 != pArg->dwNetMsgFailureReported ) ) {
LPWSTR szStringArray[3];
// Write event log warning message
szStringArray[0] = pArg->szQueryName;
szStringArray[1] = pArg->szNetName;
szStringArray[2] = FormatEventLogMessage(dwStatus);
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_NET_MESSAGE_WARNING,
NULL,
3,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
pArg->dwNetMsgFailureReported = 1;
}
} else {
// there's something flaky in the configuration
// in that the flag is set, but there's no name
// in any case, it's not worth worrying about so skip
}
}
if ((pArg->dwAlertActionFlags & ALRT_ACTION_EXEC_CMD) == ALRT_ACTION_EXEC_CMD) {
DWORD dwStatus = ERROR_SUCCESS;
dwStatus = DoAlertCommandFile (
pArg,
pAlertCI,
szTimeStamp,
szValueString,
szOverUnderString,
szLimitString);
}
if ((pArg->dwAlertActionFlags & ALRT_ACTION_START_LOG) == ALRT_ACTION_START_LOG) {
DWORD dwStatus;
// start specified perf data log
dwStatus = StartLogQuery ( pArg );
}
}
} // end of for each counter in alert loop
return TRUE;
}
BOOL
AlertProc (
IN PLOG_QUERY_DATA pArg
)
{
DWORD dwStatus = ERROR_SUCCESS;
HQUERY hQuery = NULL;
LARGE_INTEGER liStartDelayTics;
LARGE_INTEGER liSampleDelayTics;
LONGLONG llSampleCollectionTics;
LONGLONG llSampleIntervalTics;
PDH_STATUS pdhStatus = ERROR_SUCCESS;
DWORD dwCounterCount;
LPTSTR szThisPath;
BOOL bRun = FALSE;
LPTSTR szStringArray[4];
LONGLONG llSessionSampleCount=(LONGLONG)-1;
PALERT_COUNTER_INFO pCtrInfo = NULL;
PALERT_INFO_BLOCK pAlertInfo = NULL;
DWORD dwBufSize;
LONGLONG llStartTime = 0;
LONGLONG llFinishTime = 0;
__try {
#if _DEBUG_OUTPUT
{
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Thread created\n", pArg->szQueryName);
OutputDebugString (szDebugString);
}
#endif
liStartDelayTics.QuadPart = ((LONGLONG)(0));
liSampleDelayTics.QuadPart = ((LONGLONG)(0));
llSampleCollectionTics = ((LONGLONG)(0));
// Read registry values.
if ( ERROR_SUCCESS == LoadQueryConfig ( pArg ) ) {
bRun = TRUE;
}
if ( TRUE == bRun ) {
// Delay of -1 signals exit immediately.
liStartDelayTics.QuadPart = ComputeStartWaitTics ( pArg, TRUE );
if ( NULL_INTERVAL_TICS == liStartDelayTics.QuadPart ) {
bRun = FALSE;
}
}
if ( TRUE == bRun ) {
ValidateCommandFilePath ( pArg );
// open query and add counters from info file
if (pArg->dwRealTimeQuery == DATA_SOURCE_WBEM) {
pdhStatus = PdhOpenQueryH(
H_WBEM_DATASOURCE, 0, & hQuery); // from current activity
} else {
pdhStatus = PdhOpenQueryH(
H_REALTIME_DATASOURCE, 0, & hQuery);
}
if (pdhStatus != ERROR_SUCCESS) {
// unable to open query so write event log message and exit
szStringArray[0] = pArg->szQueryName;
szStringArray[1] = FormatEventLogMessage(pdhStatus);
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_OPEN_PDH_QUERY,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&pdhStatus);
bRun = FALSE;
}
}
// Add each counter and associated alert limits
if ( TRUE == bRun ) {
dwCounterCount = 0;
for (szThisPath = pArg->mszCounterList;
*szThisPath != 0;
szThisPath += lstrlen(szThisPath) + 1) {
HCOUNTER hThisCounter;
// allocate information block
dwBufSize = (lstrlenW(szThisPath) + 1) * sizeof(WCHAR);
dwBufSize += sizeof(ALERT_INFO_BLOCK);
pAlertInfo = (PALERT_INFO_BLOCK)G_ALLOC(dwBufSize);
if (pAlertInfo == NULL) {
dwStatus = SMLOG_UNABLE_ALLOC_ALERT_MEMORY;
break;
} else {
if (MakeInfoFromString (szThisPath, pAlertInfo, &dwBufSize)) {
// get alert info from string
pdhStatus = PdhAdd009Counter (hQuery,
(LPTSTR)pAlertInfo->szCounterPath,
dwCounterCount,
&hThisCounter);
if (pdhStatus != ERROR_SUCCESS) {
pdhStatus = PdhAddCounter (hQuery,
(LPTSTR)pAlertInfo->szCounterPath,
dwCounterCount,
&hThisCounter);
}
if ( !IsErrorSeverity(pdhStatus) ) {
dwCounterCount++;
if ( ERROR_SUCCESS != pdhStatus ) {
// Write event log warning message
szStringArray[0] = szThisPath;
szStringArray[1] = pArg->szQueryName;
szStringArray[2] = FormatEventLogMessage(pdhStatus);
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_ADD_COUNTER_WARNING,
NULL,
3,
sizeof(DWORD),
szStringArray,
(LPVOID)&pdhStatus);
}
// then add this handle to the list
pCtrInfo = G_ALLOC (sizeof (ALERT_COUNTER_INFO));
if (pCtrInfo != NULL) {
// insert at front of list since the order isn't
// important and this is simpler than walking the
// list each time.
pCtrInfo->hCounter = hThisCounter;
pCtrInfo->pAlertInfo = pAlertInfo;
pCtrInfo->next = (PALERT_COUNTER_INFO)pArg->pFirstCounter;
pArg->pFirstCounter = (PLOG_COUNTER_INFO)pCtrInfo;
pAlertInfo = NULL;
pCtrInfo = NULL;
} else {
dwStatus = SMLOG_UNABLE_ALLOC_ALERT_MEMORY;
G_FREE (pAlertInfo); // toss unused alert buffer
pAlertInfo = NULL;
break;
}
} else {
// unable to add the current counter so write event log message
szStringArray[0] = pAlertInfo->szCounterPath;
szStringArray[1] = pArg->szQueryName;
szStringArray[2] = FormatEventLogMessage(pdhStatus);
if ( PDH_ACCESS_DENIED == pdhStatus ) {
ReportEvent (
hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_ACCESS_COUNTER,
NULL,
2,
0,
szStringArray,
NULL);
} else {
ReportEvent (
hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_ADD_COUNTER,
NULL,
3,
sizeof(DWORD),
szStringArray,
(LPVOID)&pdhStatus);
}
if ( NULL != pAlertInfo ) {
G_FREE (pAlertInfo); // toss unused alert buffer
pAlertInfo = NULL;
}
}
} else {
// unable to parse alert info so log an error
// unable to add the current counter so write event log message
szStringArray[0] = szThisPath;
szStringArray[1] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_PARSE_ALERT_INFO,
NULL,
2,
0,
szStringArray,
NULL);
if ( NULL != pAlertInfo ) {
G_FREE (pAlertInfo); // toss unused alert buffer
pAlertInfo = NULL;
}
}
}
}
if ( ERROR_SUCCESS == dwStatus ) {
if ( 0 < dwCounterCount ) {
// to make sure we get to log the data
SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
} else {
bRun = FALSE;
// unable to add any counters so write event log message and exit.
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_ADD_ANY_COUNTERS,
NULL,
1,
0,
szStringArray,
NULL);
}
} else {
assert ( ERROR_OUTOFMEMORY == dwStatus );
// Memory allocation error so write event log message and exit.
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_ALLOC_ALERT_MEMORY,
NULL,
1,
0,
szStringArray,
NULL);
bRun = FALSE;
}
while (bRun) {
HANDLE arrEventHandle[2];
arrEventHandle[0] = pArg->hExitEvent; // WAIT_OBJECT_0
arrEventHandle[1] = pArg->hReconfigEvent;
if ( 0 < liStartDelayTics.QuadPart ) {
// NtWaitForMultipleObjects requires negative Tic value
liStartDelayTics.QuadPart = ((LONGLONG)(0)) - liStartDelayTics.QuadPart;
// Wait until specified start time, or until exit or reconfigure event.
if ( STATUS_TIMEOUT != NtWaitForMultipleObjects (
2,
&arrEventHandle[0],
WaitAny,
FALSE,
&liStartDelayTics )) {
#if _DEBUG_OUTPUT
{
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Thread received exit or reconfig event\n", pArg->szQueryName);
OutputDebugString (szDebugString);
}
#endif
bRun = FALSE;
break; // if we're not supposed to be running then bail out
}
}
pArg->dwCurrentState = SLQ_QUERY_RUNNING;
dwStatus = WriteRegistryDwordValue (
pArg->hKeyQuery,
(LPCWSTR)L"Current State",
&pArg->dwCurrentState,
REG_DWORD );
assert (dwStatus == ERROR_SUCCESS);
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_ALERT_SCANNING,
NULL,
1,
0,
szStringArray,
NULL);
// Compute session sample count.
// 0 samples signals no limit.
// -1 samples signals exit immediately
ComputeSampleCount( pArg, TRUE, &llSessionSampleCount );
if ( -1 == llSessionSampleCount ) {
goto ProcessAlertRepeat;
}
// Start sampling immediately. liSampleDelayTics is initialized to 0.
// Wait until specified sample time, or until exit or reconfigure event.
while ( STATUS_TIMEOUT == NtWaitForMultipleObjects (
2,
&arrEventHandle[0],
WaitAny,
FALSE,
&liSampleDelayTics)) {
// An event flag will be set when the sampling should exit or reconfigure. if
// the wait times out, then that means it's time to collect and
// log another sample of data.
GetLocalFileTime (&llStartTime);
// Check for reconfig event.
if ( pArg->bLoadNewConfig ) {
bRun = FALSE;
break;
}
pdhStatus = PdhCollectQueryData (hQuery);
if ( IsPdhDataCollectSuccess ( pdhStatus )
|| IsWarningSeverity ( pdhStatus ) ) {
if (pdhStatus == ERROR_SUCCESS) {
// process alert counters here
ExamineAlertValues (pArg);
}
// see if it's time to restart or end the alert scan.
// 0 samples signals no sample limit.
if ( 0 != llSessionSampleCount ) {
if ( !--llSessionSampleCount )
break;
}
} else {
// unable to collect the query data so log event and exit
szStringArray[0] = pArg->szQueryName;
szStringArray[1] = FormatEventLogMessage(pdhStatus);
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_COLLECT_DATA,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&pdhStatus);
bRun = FALSE;
break;
}
// compute new timeout value
GetLocalFileTime (&llFinishTime);
// compute difference in tics
llSampleCollectionTics = llFinishTime - llStartTime;
llSampleIntervalTics =
(LONGLONG)pArg->dwMillisecondSampleInterval*FILETIME_TICS_PER_MILLISECOND;
if ( llSampleCollectionTics < llSampleIntervalTics ) {
liSampleDelayTics.QuadPart = llSampleIntervalTics - llSampleCollectionTics;
} else {
liSampleDelayTics.QuadPart = ((LONGLONG)(0));
}
// NtWaitForMultipleObjects requires negative Tic value
liSampleDelayTics.QuadPart = ((LONGLONG)(0)) - liSampleDelayTics.QuadPart;
} // end while wait keeps timing out
// Use 0 SampleDelayTics value to check for ExitEvent.
liSampleDelayTics.QuadPart = ((LONGLONG)(0));
if ( pArg->bLoadNewConfig ) {
bRun = FALSE;
} else if ( STATUS_TIMEOUT != NtWaitForSingleObject (
pArg->hExitEvent,
FALSE,
&liSampleDelayTics ) ) {
// then the loop was terminated by the Exit event
// so clear the "run" flag to exit the loop & thread
bRun = FALSE;
#if _DEBUG_OUTPUT
{
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Thread received exit event\n", pArg->szQueryName);
OutputDebugString (szDebugString);
}
#endif
}
// If restart not enabled, then exit.
ProcessAlertRepeat:
if ( bRun ) {
bRun = ProcessRepeatOption ( pArg, &liStartDelayTics );
}
} // end while (bRun)
PdhCloseQuery (hQuery);
hQuery = NULL;
#if _DEBUG_OUTPUT
szStringArray[0] = pArg->szQueryName;
szStringArray[1] = szCurrentLogFile;
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_QUERY_STOPPED,
NULL,
2,
0,
szStringArray,
NULL);
#endif
}
SetLastError ( ERROR_SUCCESS );
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
bRun = FALSE;
if ( NULL != hQuery ) {
PdhCloseQuery ( hQuery );
hQuery = NULL;
}
SetLastError ( SMLOG_THREAD_FAILED );
}
DeallocateQueryBuffers ( pArg );
while ( NULL != pArg->pFirstCounter ) {
PALERT_COUNTER_INFO pDelCI = (PALERT_COUNTER_INFO)pArg->pFirstCounter;
if (pDelCI->pAlertInfo != NULL) G_FREE(pDelCI->pAlertInfo);
pArg->pFirstCounter = (PLOG_COUNTER_INFO)pDelCI->next;
G_FREE( pDelCI );
}
return bRun;
}
BOOL
CounterLogProc (
IN PLOG_QUERY_DATA pArg )
{
#define INSTBUFLEN 4096
HQUERY hQuery = NULL;
HLOG hLog = NULL;
LARGE_INTEGER liStartDelayTics;
LARGE_INTEGER liSampleDelayTics;
LONGLONG llSampleCollectionTics;
LONGLONG llSampleIntervalTics;
PDH_STATUS pdhStatus = ERROR_SUCCESS;
DWORD dwCounterCount;
DWORD dwStatus = ERROR_SUCCESS;
INT iCnfSerial;
DWORD dwSessionSerial;
LPTSTR szThisPath;
DWORD dwPdhLogFileType;
DWORD dwPdhAccessFlags;
BOOL bRun = FALSE;
LONGLONG llSessionSampleCount=(LONGLONG)-1;
LONGLONG llCnfSampleCount=(LONGLONG)-1;
LONGLONG llLoopSampleCount=(LONGLONG)-1;
TCHAR szCurrentLogFile[MAX_PATH+1];
LPTSTR szStringArray[4];
DWORD dwFileSizeLimit;
ULONGLONG ullFileSizeLimit;
LONGLONG llFileSize;
LONGLONG llStartTime = 0;
LONGLONG llFinishTime = 0;
HANDLE arrEventHandle[2];
PLOG_COUNTER_INFO pDelCI;
// Wildcard processing
ULONG ulBufLen;
INT nCounterBufRetry;
LPWSTR pszCounterBuf = NULL;
LPTSTR pszCounter;
DWORD dwPdhExpandFlags;
TCHAR achInfoBuf[sizeof(PDH_COUNTER_PATH_ELEMENTS) + MAX_PATH + 5];
ULONG ulBufSize;
PPDH_COUNTER_PATH_ELEMENTS pPathInfo = (PPDH_COUNTER_PATH_ELEMENTS)achInfoBuf;
__try {
#if _DEBUG_OUTPUT
{
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Thread created\n", pArg->szQueryName);
OutputDebugString (szDebugString);
}
#endif
liStartDelayTics.QuadPart = ((LONGLONG)(0));
liSampleDelayTics.QuadPart = ((LONGLONG)(0));
llSampleCollectionTics = ((LONGLONG)(0));
// Read registry values.
if ( ERROR_SUCCESS == LoadQueryConfig ( pArg ) ) {
bRun = TRUE;
}
if ( TRUE == bRun ) {
// Delay of -1 signals exit immediately.
liStartDelayTics.QuadPart = ComputeStartWaitTics ( pArg, TRUE );
if ( NULL_INTERVAL_TICS == liStartDelayTics.QuadPart ) {
bRun = FALSE;
}
}
if ( TRUE == bRun ) {
ValidateCommandFilePath ( pArg );
// open query and add counters from info file
if (pArg->dwRealTimeQuery == DATA_SOURCE_WBEM) {
pdhStatus = PdhOpenQueryH(
H_WBEM_DATASOURCE, 0, & hQuery); // from current activity
}
else {
pdhStatus = PdhOpenQueryH(
H_REALTIME_DATASOURCE, 0, & hQuery);
}
if (pdhStatus != ERROR_SUCCESS) {
// unable to open query so write event log message and exit
szStringArray[0] = pArg->szQueryName;
szStringArray[1] = FormatEventLogMessage(pdhStatus);
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_OPEN_PDH_QUERY,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&pdhStatus);
bRun = FALSE;
}
}
// Add each counter to the open query.
if ( TRUE == bRun ) {
dwStatus = ERROR_SUCCESS;
dwCounterCount = 0;
for (szThisPath = pArg->mszCounterList;
*szThisPath != 0;
szThisPath += lstrlen(szThisPath) + 1) {
if (_tcschr(szThisPath, TEXT('*')) == NULL) {
// No wildcards
dwStatus = AddCounterToCounterLog( pArg, szThisPath, hQuery, LOG_EVENT_ON_ERROR, &dwCounterCount );
} else {
// At least one wildcard
dwPdhExpandFlags = 0;
pszCounterBuf = NULL;
// Only expand wildcard instances for text log files
//
if (pArg->dwLogFileType == SLF_SQL_LOG) {
// No need to expand wildcard instances for SQL log.
// SQL log now has the capability to catch dynamic
// instances, so we can pass in wildcard-instance
// counter names here.
//
dwPdhExpandFlags |= PDH_NOEXPANDINSTANCES;
}
else if ( SLF_CSV_FILE != pArg->dwLogFileType
&& SLF_TSV_FILE != pArg->dwLogFileType) {
// This is binary counter logfile case.
// No need for expand wildcard instances, also if
// default real-time datasource is from registry (not
// WMI), we can handle add-by-object.
//
dwPdhExpandFlags |= PDH_NOEXPANDINSTANCES;
if ( DATA_SOURCE_REGISTRY == pArg->dwRealTimeQuery) {
// If both instance and counter are wildcards, then log by object
// rather than expanding the counter path.
// This is only true when the actual data source is the registry.
// Parse pathname
ZeroMemory ( achInfoBuf, sizeof(achInfoBuf) );
ulBufSize = sizeof(achInfoBuf);
pdhStatus = PdhParseCounterPath(szThisPath, pPathInfo, &ulBufSize, 0);
if (pdhStatus == ERROR_SUCCESS) {
if ( 0 == lstrcmpi ( pPathInfo->szCounterName, _T("*") ) ) {
if ( NULL != pPathInfo->szInstanceName ) {
if ( 0 == lstrcmpi ( pPathInfo->szInstanceName, _T("*") ) ) {
// If PdhAddCounter failed,the realtime data source is actually WBEM.
// In this case, expand the counter paths.
dwStatus = AddCounterToCounterLog( pArg, szThisPath, hQuery, !LOG_EVENT_ON_ERROR, &dwCounterCount );
if ( ERROR_SUCCESS == dwStatus ) {
continue;
} else {
// enumerate counter paths below and retry
dwStatus = ERROR_SUCCESS;
}
}
} else {
dwStatus = AddCounterToCounterLog( pArg, szThisPath, hQuery, !LOG_EVENT_ON_ERROR, &dwCounterCount );
// If PdhAddCounter failed,the realtime data source is actually WBEM.
// In this case, expand the counter paths.
if ( ERROR_SUCCESS == dwStatus ) {
continue;
} else {
// enumerate counter paths below and retry
dwStatus = ERROR_SUCCESS;
}
}
}
} else {
// Report event and continue to next counter
szStringArray[0] = szThisPath;
szStringArray[1] = pArg->szQueryName;
szStringArray[2] = FormatEventLogMessage(pdhStatus);
ReportEvent (
hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_PARSE_COUNTER,
NULL,
3,
sizeof(DWORD),
szStringArray,
(LPVOID)&pdhStatus);
continue;
}
}
}
// Log by object paths are already processed. For other paths with at least
// one wildcard, expand the path before adding counters.
ulBufLen = INSTBUFLEN;
nCounterBufRetry = 10; // the retry counter
do {
if ( NULL != pszCounterBuf ) {
G_FREE(pszCounterBuf);
pszCounterBuf = NULL;
ulBufLen *= 2;
}
pszCounterBuf = (WCHAR*) G_ALLOC(ulBufLen * sizeof(TCHAR));
if (pszCounterBuf == NULL) {
dwStatus = ERROR_OUTOFMEMORY;
break;
}
pdhStatus = PdhExpandWildCardPath (
NULL,
(LPWSTR)szThisPath,
(LPWSTR)pszCounterBuf,
&ulBufLen,
dwPdhExpandFlags);
nCounterBufRetry--;
} while ((pdhStatus == PDH_MORE_DATA) && (nCounterBufRetry));
if (ERROR_SUCCESS == pdhStatus && ERROR_SUCCESS == dwStatus ) {
// Add path
for (pszCounter = pszCounterBuf;
*pszCounter != 0;
pszCounter += lstrlen(pszCounter) + 1) {
dwStatus = AddCounterToCounterLog ( pArg, pszCounter, hQuery, LOG_EVENT_ON_ERROR, &dwCounterCount );
if ( ERROR_OUTOFMEMORY == dwStatus ) {
break;
}
}
}
if ( NULL != pszCounterBuf ) {
G_FREE(pszCounterBuf);
pszCounterBuf = NULL;
}
}
if ( ERROR_OUTOFMEMORY == dwStatus ) {
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_ALLOC_LOG_MEMORY,
NULL,
1,
0,
szStringArray,
NULL);
bRun = FALSE;
} // Other errors reported within the loop
}
if ( bRun ) {
if ( 0 < dwCounterCount ) {
// to make sure we get to log the data
SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
} else {
bRun = FALSE;
// unable to add any counters so write event log message and exit.
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_ADD_ANY_COUNTERS,
NULL,
1,
0,
szStringArray,
NULL);
}
}
while (bRun) {
arrEventHandle[0] = pArg->hExitEvent; // WAIT_OBJECT_0
arrEventHandle[1] = pArg->hReconfigEvent;
// Wait until specified start time, or until exit or reconfig event.
if ( 0 < liStartDelayTics.QuadPart ) {
// NtWaitForMultipleObjects requires negative Tic value
liStartDelayTics.QuadPart = ((LONGLONG)(0)) - liStartDelayTics.QuadPart;
if ( STATUS_TIMEOUT != NtWaitForMultipleObjects (
2,
&arrEventHandle[0],
WaitAny,
FALSE,
&liStartDelayTics)) {
#if _DEBUG_OUTPUT
{
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Thread received reconfig or exit event\n", pArg->szQueryName);
OutputDebugString (szDebugString);
}
#endif
bRun = FALSE;
break; // if we're not supposed to be running then bail out
}
}
// Compute session sample count.
// 0 samples signals no limit.
// -1 samples signals exit immediately, because stop time is past.
ComputeSampleCount( pArg, TRUE, &llSessionSampleCount );
if ( (LONGLONG)(-1) == llSessionSampleCount ) {
goto ProcessCounterRepeat;
}
// Set session or cnf file size limit.
if ( SLQ_DISK_MAX_SIZE != pArg->dwMaxFileSize )
dwFileSizeLimit = pArg->dwMaxFileSize * pArg->dwLogFileSizeUnit;
else
dwFileSizeLimit = 0;
// 0 file size signals no limit.
// Translate from DWORD to ULONGLONG instead of LONGLONG to preserve
// positive value, even if high bit of dword is used.
ullFileSizeLimit = ((ULONGLONG)(dwFileSizeLimit));
ComputeSampleCount( pArg, FALSE, &llCnfSampleCount );
if ( (LONGLONG)(-1) == llCnfSampleCount ) {
// Todo cnf: Internal program error, report error and exit.
bRun = FALSE;
break;
}
if ( SLQ_AUTO_MODE_AFTER == pArg->stiCreateNewFile.dwAutoMode
|| SLQ_AUTO_MODE_SIZE == pArg->stiCreateNewFile.dwAutoMode ) {
iCnfSerial = 1;
} else {
assert ( SLQ_AUTO_MODE_NONE == pArg->stiCreateNewFile.dwAutoMode );
iCnfSerial = 0;
}
dwSessionSerial = pArg->dwCurrentSerialNumber;
BuildCurrentLogFileName (
pArg->szQueryName,
pArg->szBaseFileName,
pArg->szLogFileFolder,
pArg->szSqlLogName,
szCurrentLogFile,
&dwSessionSerial,
pArg->dwAutoNameFormat,
pArg->dwLogFileType,
iCnfSerial++ );
// update log serial number if modified.
if (pArg->dwAutoNameFormat == SLF_NAME_NNNNNN) {
pArg->dwCurrentSerialNumber++;
// Todo: Info event on number wrap - Server Beta 3.
if ( MAXIMUM_SERIAL_NUMBER < pArg->dwCurrentSerialNumber ) {
pArg->dwCurrentSerialNumber = MINIMUM_SERIAL_NUMBER;
}
dwStatus = RegSetValueEx (
pArg->hKeyQuery,
(LPCTSTR)TEXT("Log File Serial Number"),
0L,
REG_DWORD,
(LPBYTE)&pArg->dwCurrentSerialNumber,
sizeof(DWORD));
assert ( ERROR_SUCCESS == dwStatus );
}
SetPdhOpenOptions ( pArg, &dwPdhAccessFlags, &dwPdhLogFileType );
// Create new file loop
while ( bRun && (LONGLONG)(-1) != llSessionSampleCount ) {
assert ( (LONGLONG)(-1) != llCnfSampleCount );
// Compute cnf or session loop interval
if ( (LONGLONG)(0) == llCnfSampleCount
|| ( (LONGLONG)(0) != llSessionSampleCount
&& llCnfSampleCount > llSessionSampleCount ) )
{
// No need to create new file within session
llLoopSampleCount = llSessionSampleCount;
// Specify exit after first loop if not cnf by size
if ( SLQ_AUTO_MODE_SIZE != pArg->stiCreateNewFile.dwAutoMode ) {
llSessionSampleCount = (LONGLONG)(-1);
}
} else {
// Create new file by time before session ends.
llLoopSampleCount = llCnfSampleCount;
if ( (LONGLONG)(0) != llSessionSampleCount ) {
llSessionSampleCount -= llCnfSampleCount;
// todo cnf: The following should be logically impossible,
// because session > newfile wait.
if ( llSessionSampleCount <= (LONGLONG)(0) ) {
llSessionSampleCount = (LONGLONG)(-1);
}
}
}
__try {
// Open log file using this query
// For text files, max size is checked after each data collection
pdhStatus = PdhOpenLog (
szCurrentLogFile,
dwPdhAccessFlags,
&dwPdhLogFileType,
hQuery,
( SLF_BIN_CIRC_FILE == pArg->dwLogFileType
|| SLF_BIN_FILE == pArg->dwLogFileType
|| SLF_SQL_LOG == pArg->dwLogFileType )
? dwFileSizeLimit
: 0,
( ( PDH_LOG_TYPE_BINARY != dwPdhLogFileType ) ? pArg->szLogFileComment : NULL ),
&hLog);
} __except (EXCEPTION_EXECUTE_HANDLER) {
pdhStatus = PDH_INVALID_ARGUMENT;
}
if ( ERROR_SUCCESS != pdhStatus ) {
// unable to open log file so log event log message
szStringArray[0] = szCurrentLogFile;
szStringArray[1] = pArg->szQueryName;
szStringArray[2] = FormatEventLogMessage(pdhStatus);
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_OPEN_LOG_FILE,
NULL,
3,
sizeof(DWORD),
szStringArray,
(LPVOID)&pdhStatus);
bRun = FALSE; // exit now
break;
} else {
RegisterCurrentFile( pArg->hKeyQuery, szCurrentLogFile, 0 );
pArg->dwCurrentState = SLQ_QUERY_RUNNING;
dwStatus = WriteRegistryDwordValue (
pArg->hKeyQuery,
(LPCWSTR)L"Current State",
&pArg->dwCurrentState,
REG_DWORD );
assert (dwStatus == ERROR_SUCCESS);
szStringArray[0] = pArg->szQueryName;
szStringArray[1] = szCurrentLogFile;
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_LOGGING_QUERY,
NULL,
2,
0,
szStringArray,
NULL);
}
// Start sampling immediately. liSampleDelayTics is initialized to 0.
while ( STATUS_TIMEOUT == NtWaitForMultipleObjects (
2,
&arrEventHandle[0],
WaitAny,
FALSE,
&liSampleDelayTics)) {
// An event flag will be set when the sampling should exit or reconfigure. if
// the wait times out, then that means it's time to collect and
// log another sample of data.
GetLocalFileTime (&llStartTime);
// Check for reconfig event.
if ( pArg->bLoadNewConfig ) {
bRun = FALSE;
break;
}
pdhStatus = PdhUpdateLog (hLog, pArg->szLogFileComment );
if ( IsPdhDataCollectSuccess ( pdhStatus )
|| IsWarningSeverity ( pdhStatus ) ) {
// see if it's time to restart or end the log.
// 0 samples signals no sample limit.
if ( ((LONGLONG)0) != llLoopSampleCount ) {
if ( !--llLoopSampleCount )
break;
}
if ( ( ((ULONGLONG)0) != ullFileSizeLimit )
&& ( SLF_BIN_CIRC_FILE != pArg->dwLogFileType ) ) {
// see if the file is too big
pdhStatus = PdhGetLogFileSize (hLog, &llFileSize);
if (pdhStatus == ERROR_SUCCESS) {
if (ullFileSizeLimit <= (ULONGLONG)llFileSize)
break;
}
}
} else {
// unable to update the log so log event and exit
szStringArray[0] = pArg->szQueryName;
szStringArray[1] = FormatEventLogMessage(pdhStatus);
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_UPDATE_LOG,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&pdhStatus);
bRun = FALSE;
break;
}
// compute new timeout value
GetLocalFileTime (&llFinishTime);
// compute difference in tics
llSampleCollectionTics = llFinishTime - llStartTime;
llSampleIntervalTics =
(LONGLONG)pArg->dwMillisecondSampleInterval*FILETIME_TICS_PER_MILLISECOND;
if ( llSampleCollectionTics < llSampleIntervalTics ) {
liSampleDelayTics.QuadPart = llSampleIntervalTics - llSampleCollectionTics;
} else {
liSampleDelayTics.QuadPart = ((LONGLONG)(0));
}
// NtWaitForMultipleObjects requires negative Tic value
liSampleDelayTics.QuadPart = ((LONGLONG)(0)) - liSampleDelayTics.QuadPart;
} // end while wait keeps timing out
// Use 0 SampleDelayTics value to check for ExitEvent.
liSampleDelayTics.QuadPart = ((LONGLONG)(0));
if ( pArg->bLoadNewConfig ) {
bRun = FALSE;
} else if ( STATUS_TIMEOUT != NtWaitForSingleObject (
pArg->hExitEvent,
FALSE,
&liSampleDelayTics ) ) {
// then the loop was terminated by the Exit event
// so clear the "run" flag to exit the loop & thread
bRun = FALSE;
#if _DEBUG_OUTPUT
{
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Thread received exit event\n", pArg->szQueryName);
OutputDebugString (szDebugString);
}
#endif
}
// close log file, but keep query open
PdhCloseLog (hLog, 0);
hLog = NULL;
if ( pArg->bLoadNewConfig )
break;
if ( pArg->szCmdFileName != NULL )
DoLogCommandFile (pArg, szCurrentLogFile, bRun);
if ( (LONGLONG)(-1) != llSessionSampleCount ) {
// Create new log name
BuildCurrentLogFileName (
pArg->szQueryName,
pArg->szBaseFileName,
pArg->szLogFileFolder,
pArg->szSqlLogName,
szCurrentLogFile,
&dwSessionSerial,
pArg->dwAutoNameFormat,
pArg->dwLogFileType,
iCnfSerial++ );
// Todo cnf: report event on error;
}
} // End of log file creation while loop
// cnf Todo: Handle break from sample loop. ?
// If restart not enabled, then exit.
ProcessCounterRepeat:
if ( bRun ) {
bRun = ProcessRepeatOption ( pArg, &liStartDelayTics );
}
} // end while (bRun)
PdhCloseQuery (hQuery);
hQuery = NULL;
#if _DEBUG_OUTPUT
szStringArray[0] = pArg->szQueryName;
szStringArray[1] = szCurrentLogFile;
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_QUERY_STOPPED,
NULL,
2,
0,
szStringArray,
NULL);
#endif
}
SetLastError ( ERROR_SUCCESS );
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
bRun = FALSE;
if ( NULL != pszCounterBuf ) {
G_FREE(pszCounterBuf);
pszCounterBuf = NULL;
}
if ( NULL != hLog ) {
PdhCloseLog ( hLog, 0 );
hLog = NULL;
}
if ( NULL != hQuery ) {
PdhCloseQuery ( hQuery );
hQuery = NULL;
}
SetLastError ( SMLOG_THREAD_FAILED );
}
DeallocateQueryBuffers ( pArg );
while ( NULL != pArg->pFirstCounter ) {
pDelCI = pArg->pFirstCounter;
pArg->pFirstCounter = pDelCI->next;
G_FREE( pDelCI );
}
return bRun;
}
BOOL
TraceLogProc (
IN PLOG_QUERY_DATA pArg
)
{
LARGE_INTEGER liStartDelayTics;
LARGE_INTEGER liWaitTics;
LONGLONG llSessionWaitTics = 0;
LONGLONG llNewFileWaitTics = INFINITE_TICS;
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwIndex;
BOOL bRun = FALSE;
BOOL bStarted = FALSE;
LPTSTR szStringArray[4];
TCHAR szCurrentLogFile[MAX_PATH+1];
INT iCnfSerial = 0;
ULONG ulIndex;
int iEnableCount = 0;
DWORD dwSessionSerial;
HANDLE arrEventHandle[2];
__try {
#if _DEBUG_OUTPUT
{
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Thread created\n", pArg->szQueryName);
OutputDebugString (szDebugString);
}
#endif
liStartDelayTics.QuadPart = NULL_INTERVAL_TICS;
liWaitTics.QuadPart = ((LONGLONG)(0));
// Read registry values.
if ( ERROR_SUCCESS == LoadQueryConfig ( pArg ) ) {
bRun = TRUE;
}
if ( TRUE == bRun ) {
// Delay of -1 signals exit immediately.
liStartDelayTics.QuadPart = ComputeStartWaitTics ( pArg, TRUE );
if ( NULL_INTERVAL_TICS == liStartDelayTics.QuadPart ) {
bRun = FALSE;
}
}
if ( bRun ) {
ValidateCommandFilePath ( pArg );
// to make sure we get to log the data
SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
}
while (bRun) {
arrEventHandle[0] = pArg->hExitEvent; // WAIT_OBJECT_0
arrEventHandle[1] = pArg->hReconfigEvent;
if ( 0 < liStartDelayTics.QuadPart ) {
// NtWaitForMultipleObjects requires negative Tic value
liStartDelayTics.QuadPart = ((LONGLONG)(0)) - liStartDelayTics.QuadPart;
// Wait until specified start time, or until exit or reconfig event.
if ( STATUS_TIMEOUT != NtWaitForMultipleObjects (
2,
&arrEventHandle[0],
WaitAny,
FALSE,
&liStartDelayTics)) {
#if _DEBUG_OUTPUT
{
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Thread received exit or reconfig event\n", pArg->szQueryName);
OutputDebugString (szDebugString);
}
#endif
bRun = FALSE; // if we're not supposed to be running then bail out
break;
}
}
ComputeSessionTics( pArg, &llSessionWaitTics );
// 0 signals no session time, so exit.
if ( ((LONGLONG)(0)) == llSessionWaitTics ) {
goto ProcessTraceRepeat;
}
// llNewFileWaitTics defaults to -1 if no time limit.
ComputeNewFileTics( pArg, &llNewFileWaitTics );
// InitTraceProperties creates the current file name
dwSessionSerial = pArg->dwCurrentSerialNumber;
InitTraceProperties ( pArg, TRUE, &dwSessionSerial, &iCnfSerial );
dwStatus = GetTraceQueryStatus ( pArg, NULL );
// If trace session with this name already started and successful,
// don't create another session.
if ( ERROR_SUCCESS != dwStatus ) {
dwStatus = StartTrace(
&pArg->LoggerHandle,
pArg->szLoggerName,
&pArg->Properties );
if (dwStatus == ERROR_SUCCESS) {
bStarted = TRUE;
}
if ( ( ERROR_SUCCESS == dwStatus )
&& !( pArg->Properties.EnableFlags & EVENT_TRACE_FLAG_PROCESS
|| pArg->Properties.EnableFlags & EVENT_TRACE_FLAG_THREAD
|| pArg->Properties.EnableFlags & EVENT_TRACE_FLAG_DISK_IO
|| pArg->Properties.EnableFlags & EVENT_TRACE_FLAG_NETWORK_TCPIP ) ) {
for ( ulIndex = 0; ulIndex < pArg->ulGuidCount; ulIndex++ ) {
// Enable user mode and special kernel tracing.
dwStatus = EnableTrace (
TRUE,
0,
0,
pArg->arrpGuid[ulIndex],
pArg->LoggerHandle);
if ( ERROR_SUCCESS == dwStatus ) {
iEnableCount++;
} else {
szStringArray[0] = pArg->arrpszProviderName[ulIndex];
szStringArray[1] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_ENABLE_TRACE_PROV,
NULL,
2,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus);
}
}
if ( 0 < iEnableCount ) {
dwStatus = ERROR_SUCCESS;
} else {
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_TRACE_NO_PROVIDERS,
NULL,
1,
0,
szStringArray,
NULL);
bRun = FALSE;
}
}
if ( bRun && ERROR_SUCCESS == dwStatus ) {
pArg->dwCurrentState = SLQ_QUERY_RUNNING;
dwStatus = WriteRegistryDwordValue (
pArg->hKeyQuery,
(LPCTSTR)L"Current State",
&pArg->dwCurrentState,
REG_DWORD );
szStringArray[0] = pArg->szQueryName;
szStringArray[1] = pArg->szLogFileName;
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_LOGGING_QUERY,
NULL,
2,
0,
szStringArray,
NULL);
} else {
//StartTraceFailed
//dwStatus should be ERROR_ALREADY_EXISTS if logger already started or anything else
if ( ERROR_ALREADY_EXISTS == dwStatus ) {
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_TRACE_ALREADY_RUNNING,
NULL,
1,
0,
szStringArray,
NULL);
} else {
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_UNABLE_START_TRACE,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&dwStatus );
}
bRun = FALSE;
}
} else {
// This means that QueryTrace Returned Error Success.
// The specified logger is already running.
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_TRACE_ALREADY_RUNNING,
NULL,
1,
0,
szStringArray,
NULL);
bRun = FALSE;
}
if ( TRUE == bRun ) {
// Trace logger is now running.
// Exit when:
// Wait times out,
// Exit event signaled, or
// Reconfig event signaled.
// -1 wait time signals no limit.
// Loop wait intervals, calculating interval before each wait.
while ( ((LONGLONG)(0)) != llSessionWaitTics ) {
// Calculate wait interval.
if ( INFINITE_TICS == llNewFileWaitTics
|| ( INFINITE_TICS != llSessionWaitTics
&& llNewFileWaitTics > llSessionWaitTics ) ) {
// No need to create new file within session
if ( INFINITE_TICS == llSessionWaitTics ) {
liWaitTics.QuadPart = llSessionWaitTics;
// Exit after first loop
llSessionWaitTics = 0;
} else {
liWaitTics.QuadPart = llSessionWaitTics;
// Exit after first loop
llSessionWaitTics = 0;
}
} else {
// Create new file before session ends.
liWaitTics.QuadPart = llNewFileWaitTics;
if ( INFINITE_TICS != llSessionWaitTics ) {
llSessionWaitTics -= llNewFileWaitTics;
// todo cnf: The following should be logically impossible,
// because session > newfile wait.
if ( 0 > llSessionWaitTics ) {
llSessionWaitTics = 0;
}
}
}
// NtWaitForMultipleObjects requires negative Tic value
if ( INFINITE_TICS != liWaitTics.QuadPart ) {
liWaitTics.QuadPart = ((LONGLONG)(0)) - liWaitTics.QuadPart;
}
if ( STATUS_TIMEOUT != NtWaitForMultipleObjects (
2,
arrEventHandle,
WaitAny,
FALSE,
( INFINITE_TICS != liWaitTics.QuadPart ) ? &liWaitTics : NULL ))
{
bRun = FALSE;
break;
} else {
// If cnf by time, llNewFileWaitTics will not be infinite
if ( INFINITE_TICS != llNewFileWaitTics
&& ((LONGLONG)(0)) != llSessionWaitTics ) {
// Time to create a new file. Don't update the autoformat
// serial number. Use the initial autoformat serial number
InitTraceProperties ( pArg, FALSE, &dwSessionSerial, &iCnfSerial );
dwStatus = UpdateTrace(
pArg->LoggerHandle,
pArg->szLoggerName,
&pArg->Properties );
// Todo cnf report event on bad status.
}
}
}
}
#if _DEBUG_OUTPUT
liWaitTics.QuadPart = ((LONGLONG)(0));
if ( pArg->bLoadNewConfig ) {
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Thread received reconfig event\n", pArg->szQueryName);
OutputDebugString (szDebugString);
} else if ( STATUS_TIMEOUT != NtWaitForSingleObject (pArg->hExitEvent, FALSE, &liWaitTics )) {
TCHAR szDebugString[MAX_PATH];
swprintf (szDebugString, (LPCWSTR)L" Query %s: Thread received exit event\n", pArg->szQueryName);
OutputDebugString (szDebugString);
}
#endif
if (bStarted == TRUE) {
// Stop the query.
if ( !( pArg->Properties.EnableFlags & EVENT_TRACE_FLAG_PROCESS
|| pArg->Properties.EnableFlags & EVENT_TRACE_FLAG_THREAD
|| pArg->Properties.EnableFlags & EVENT_TRACE_FLAG_DISK_IO
|| pArg->Properties.EnableFlags & EVENT_TRACE_FLAG_NETWORK_TCPIP ) ) {
for (dwIndex = 0; dwIndex < pArg->ulGuidCount; dwIndex++) {
dwStatus = EnableTrace (
FALSE,
0,
0,
pArg->arrpGuid[dwIndex],
pArg->LoggerHandle);
}
}
dwStatus = StopTrace (
pArg->LoggerHandle,
pArg->szLoggerName,
&pArg->Properties );
}
if ( pArg->bLoadNewConfig )
break;
if ( pArg->szCmdFileName != NULL )
DoLogCommandFile (pArg, szCurrentLogFile, bRun);
// If restart not enabled, then exit.
ProcessTraceRepeat:
if ( bRun ) {
bRun = ProcessRepeatOption ( pArg, &liStartDelayTics );
}
} // end while (bRun)
#if _DEBUG_OUTPUT
szStringArray[0] = pArg->szQueryName;
szStringArray[1] = szCurrentLogFile;
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
SMLOG_QUERY_STOPPED,
NULL,
2,
0,
szStringArray,
NULL);
#endif
SetLastError ( ERROR_SUCCESS );
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
bRun = FALSE;
SetLastError ( SMLOG_THREAD_FAILED );
}
return bRun;
}
DWORD
LoggingThreadProc (
IN LPVOID lpThreadArg
)
{
PLOG_QUERY_DATA pThreadData = (PLOG_QUERY_DATA)lpThreadArg;
DWORD dwStatus = ERROR_SUCCESS;
HRESULT hr = NOERROR;
BOOL bContinue = TRUE;
LPWSTR szStringArray[2];
if (pThreadData != NULL) {
__try {
hr = PdhiPlaRunAs( pThreadData->szQueryName, NULL, &pThreadData->hUserToken );
if( ERROR_SUCCESS != hr ){
szStringArray[0] = pThreadData->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
SMLOG_INVALID_CREDENTIALS,
NULL,
1,
sizeof(HRESULT),
szStringArray,
(LPVOID)&hr
);
return hr;
}
do {
// read config from registry
// expand counter paths as necessary
if (pThreadData->dwLogType == SLQ_ALERT) {
// call Alert procedure
bContinue = AlertProc (pThreadData);
} else if (pThreadData->dwLogType == SLQ_COUNTER_LOG) {
// call Logging procedure
bContinue = CounterLogProc (pThreadData);
} else if (pThreadData->dwLogType == SLQ_TRACE_LOG) {
// call Logging procedure
bContinue = TraceLogProc (pThreadData);
} else {
assert (FALSE); // incorrect log type for this function
}
// see if this thread was paused for reloading
// or stopped to terminate
if (pThreadData->bLoadNewConfig) {
bContinue = TRUE;
// Reset the reconfig flag and event.
pThreadData->bLoadNewConfig = FALSE;
ResetEvent ( pThreadData->hReconfigEvent );
} // else bContinue is always returned as FALSE
// so that will terminate this loop
} while (bContinue);
dwStatus = GetLastError();
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
dwStatus = SMLOG_THREAD_FAILED;
}
} else {
// unable to find data block so return
dwStatus = ERROR_INVALID_PARAMETER;
}
if ( ERROR_SUCCESS != dwStatus ) {
szStringArray[0] = pThreadData->szQueryName;
ReportEvent (
hEventLog,
EVENTLOG_WARNING_TYPE,
0,
dwStatus,
NULL,
1,
0,
szStringArray,
NULL
);
}
return dwStatus;
}