902 lines
26 KiB
C
902 lines
26 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1996 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
LogThred.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
module containing logging thread functions
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Bob Watson (a-robw) 10 Apr 96
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
#ifndef UNICODE
|
|||
|
#define UNICODE 1
|
|||
|
#endif
|
|||
|
#ifndef _UNICODE
|
|||
|
#define _UNICODE 1
|
|||
|
#endif
|
|||
|
//
|
|||
|
// Windows Include files
|
|||
|
//
|
|||
|
#include <windows.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <tchar.h>
|
|||
|
#include <pdh.h>
|
|||
|
#include <pdhmsg.h>
|
|||
|
#include "pdlsvc.h"
|
|||
|
//#include "logutils.h"
|
|||
|
#include "pdlmsg.h"
|
|||
|
|
|||
|
static
|
|||
|
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 % 100 != 0 && pST->wYear % 4 == 0 ) {
|
|||
|
// this is a leap year
|
|||
|
JDate += wDaysInLeapYearMonth[pST->wMonth - 1];
|
|||
|
} else {
|
|||
|
// this is not a leap year
|
|||
|
JDate += wDaysInRegularMonth[pST->wMonth - 1];
|
|||
|
}
|
|||
|
}
|
|||
|
// Add in days for this month.
|
|||
|
JDate += pST->wDay;
|
|||
|
|
|||
|
// Add in year.
|
|||
|
JDate += (pST->wYear % 100) * 10000;
|
|||
|
|
|||
|
return JDate;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
BOOL
|
|||
|
GetLocalFileTime (
|
|||
|
SYSTEMTIME *pST,
|
|||
|
LONGLONG *pFileTime
|
|||
|
)
|
|||
|
{
|
|||
|
BOOL bResult;
|
|||
|
GetLocalTime (pST);
|
|||
|
if (pFileTime != NULL) {
|
|||
|
bResult = SystemTimeToFileTime (pST, (LPFILETIME)pFileTime);
|
|||
|
} else {
|
|||
|
bResult = TRUE;
|
|||
|
}
|
|||
|
return bResult;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
DWORD
|
|||
|
GetSamplesInRenameInterval(
|
|||
|
IN DWORD dwSampleInterval, // in seconds
|
|||
|
IN DWORD dwRenameIntervalCount, // in units
|
|||
|
IN DWORD dwRenameIntervalUnits) // for "count" arg
|
|||
|
{
|
|||
|
DWORD dwRenameIntervalSeconds;
|
|||
|
// convert rename interval to seconds and account for the
|
|||
|
// first (or zero-th) sample in the log
|
|||
|
|
|||
|
dwRenameIntervalSeconds = dwRenameIntervalCount;
|
|||
|
|
|||
|
switch (dwRenameIntervalUnits) {
|
|||
|
case OPD_RENAME_HOURS:
|
|||
|
dwRenameIntervalSeconds *= SECONDS_IN_HOUR;
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_RENAME_DAYS:
|
|||
|
default:
|
|||
|
dwRenameIntervalSeconds *= SECONDS_IN_DAY;
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_RENAME_MONTHS:
|
|||
|
dwRenameIntervalSeconds *= SECONDS_IN_DAY * 30;
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_RENAME_KBYTES:
|
|||
|
case OPD_RENAME_MBYTES:
|
|||
|
// these don't use a rename counter
|
|||
|
return (DWORD)0;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
dwRenameIntervalSeconds /= dwSampleInterval;
|
|||
|
|
|||
|
dwRenameIntervalSeconds += 1; // add in the "zero-th" sample
|
|||
|
|
|||
|
return (dwRenameIntervalSeconds);
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
LONG
|
|||
|
BuildCurrentLogFileName (
|
|||
|
IN LPCTSTR szBaseFileName,
|
|||
|
IN LPCTSTR szDefaultDir,
|
|||
|
IN LPTSTR szOutFileBuffer,
|
|||
|
IN LPDWORD lpdwSerialNumber,
|
|||
|
IN DWORD dwDateFormat,
|
|||
|
IN DWORD dwLogFormat
|
|||
|
)
|
|||
|
// presumes OutFileBuffer is large enough (i.e. >= MAX_PATH)
|
|||
|
{
|
|||
|
SYSTEMTIME st;
|
|||
|
BOOL bUseCurrentDir = FALSE;
|
|||
|
TCHAR szAuto[MAX_PATH];
|
|||
|
LPTSTR szExt;
|
|||
|
|
|||
|
if (szDefaultDir != NULL) {
|
|||
|
if (*szDefaultDir == 0) {
|
|||
|
bUseCurrentDir = TRUE;
|
|||
|
}
|
|||
|
} else {
|
|||
|
bUseCurrentDir = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (bUseCurrentDir) {
|
|||
|
GetCurrentDirectory (MAX_PATH, szOutFileBuffer);
|
|||
|
} else {
|
|||
|
lstrcpy (szOutFileBuffer, szDefaultDir);
|
|||
|
}
|
|||
|
|
|||
|
// add a backslash to the path name if it doesn't have one already
|
|||
|
|
|||
|
if (szOutFileBuffer[lstrlen(szOutFileBuffer)-1] != TEXT('\\')) {
|
|||
|
lstrcat (szOutFileBuffer, TEXT("\\"));
|
|||
|
}
|
|||
|
|
|||
|
// add the base filename
|
|||
|
|
|||
|
lstrcat (szOutFileBuffer, szBaseFileName);
|
|||
|
|
|||
|
// add the auto name part
|
|||
|
|
|||
|
// get date/time/serial integer format
|
|||
|
GetLocalTime(&st);
|
|||
|
|
|||
|
switch (dwDateFormat) {
|
|||
|
case OPD_NAME_NNNNNN:
|
|||
|
_stprintf (szAuto, TEXT("_%6.6d"), *lpdwSerialNumber);
|
|||
|
(*lpdwSerialNumber)++; // increment
|
|||
|
if (*lpdwSerialNumber >= 1000000) {
|
|||
|
// roll over to 0
|
|||
|
*lpdwSerialNumber = 0;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_NAME_YYDDD:
|
|||
|
_stprintf (szAuto, TEXT("_%5.5d"),
|
|||
|
JulianDateFromSystemTime(&st));
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_NAME_YYMM:
|
|||
|
_stprintf (szAuto, TEXT("_%2.2d%2.2d"),
|
|||
|
st.wYear % 100, st.wMonth);
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_NAME_YYMMDDHH:
|
|||
|
_stprintf (szAuto, TEXT("_%2.2d%2.2d%2.2d%2.2d"),
|
|||
|
(st.wYear % 100), st.wMonth, st.wDay, st.wHour);
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_NAME_MMDDHH:
|
|||
|
_stprintf (szAuto, TEXT("_%2.2d%2.2d%2.2d"),
|
|||
|
st.wMonth, st.wDay, st.wHour);
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_NAME_YYMMDD:
|
|||
|
default:
|
|||
|
_stprintf (szAuto, TEXT("_%2.2d%2.2d%2.2d"),
|
|||
|
st.wYear % 100, st.wMonth, st.wDay);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
lstrcat (szOutFileBuffer, szAuto);
|
|||
|
|
|||
|
// get file type
|
|||
|
switch (dwLogFormat) {
|
|||
|
case PDH_LOG_TYPE_TSV:
|
|||
|
szExt = TEXT(".tsv");
|
|||
|
break;
|
|||
|
|
|||
|
case PDH_LOG_TYPE_BINARY:
|
|||
|
szExt = TEXT(".blg");
|
|||
|
break;
|
|||
|
|
|||
|
case PDH_LOG_TYPE_CSV:
|
|||
|
default:
|
|||
|
szExt = TEXT(".csv");
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
lstrcat (szOutFileBuffer, szExt);
|
|||
|
|
|||
|
return ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
BOOL
|
|||
|
LoadDataFromRegistry (
|
|||
|
IN LPLOG_THREAD_DATA pArg,
|
|||
|
IN LPTSTR szDefaultDir,
|
|||
|
IN LPTSTR szBaseName,
|
|||
|
IN LPTSTR szCurrentLogFile
|
|||
|
)
|
|||
|
{
|
|||
|
LONG lStatus;
|
|||
|
DWORD dwType;
|
|||
|
DWORD dwSize;
|
|||
|
DWORD dwData;
|
|||
|
LPTSTR szStringArray[2];
|
|||
|
|
|||
|
// get size of buffer required by counter list,
|
|||
|
// then allocate the buffer and retrieve the counter list
|
|||
|
|
|||
|
dwType = 0;
|
|||
|
dwData = 0;
|
|||
|
dwSize = 0;
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Counter List"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)NULL,
|
|||
|
&dwSize);
|
|||
|
|
|||
|
pArg->mszCounterList = (LPTSTR)G_ALLOC(dwSize);
|
|||
|
|
|||
|
if (pArg->mszCounterList != NULL) {
|
|||
|
dwType = 0;
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Counter List"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)pArg->mszCounterList,
|
|||
|
&dwSize);
|
|||
|
|
|||
|
if ((lStatus != ERROR_SUCCESS) || (dwSize == 0)) {
|
|||
|
// no counter list retrieved so there's not much
|
|||
|
// point in continuing
|
|||
|
szStringArray[0] = pArg->szQueryName;
|
|||
|
ReportEvent (hEventLog,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
0,
|
|||
|
PERFLOG_UNABLE_READ_COUNTER_LIST,
|
|||
|
NULL,
|
|||
|
1,
|
|||
|
sizeof(DWORD),
|
|||
|
szStringArray,
|
|||
|
(LPVOID)&lStatus);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
} else {
|
|||
|
szStringArray[0] = pArg->szQueryName;
|
|||
|
ReportEvent (hEventLog,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
0,
|
|||
|
PERFLOG_UNABLE_ALLOC_COUNTER_LIST,
|
|||
|
NULL,
|
|||
|
1,
|
|||
|
sizeof(DWORD),
|
|||
|
szStringArray,
|
|||
|
(LPVOID)&lStatus);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
dwType = 0;
|
|||
|
dwData = 0;
|
|||
|
dwSize = sizeof(DWORD);
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Auto Name Interval"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&dwData,
|
|||
|
&dwSize);
|
|||
|
if (lStatus != ERROR_SUCCESS) {
|
|||
|
dwData = 0; // default is no autonaming
|
|||
|
} else if (dwType != REG_DWORD) {
|
|||
|
dwData = 0; // default is no autonaming
|
|||
|
} // else assume success
|
|||
|
|
|||
|
pArg->dwRenameIntervalCount = dwData;
|
|||
|
|
|||
|
dwType = 0;
|
|||
|
dwData = 0;
|
|||
|
dwSize = sizeof(DWORD);
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Auto Rename Units"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&dwData,
|
|||
|
&dwSize);
|
|||
|
if (lStatus != ERROR_SUCCESS) {
|
|||
|
dwData = OPD_RENAME_DAYS; // default is days
|
|||
|
} else if (dwType != REG_DWORD) {
|
|||
|
dwData = OPD_RENAME_DAYS; // default is days
|
|||
|
} // else assume success
|
|||
|
|
|||
|
pArg->dwRenameIntervalUnits = dwData;
|
|||
|
|
|||
|
dwType = 0;
|
|||
|
dwData = 0;
|
|||
|
dwSize = sizeof(DWORD);
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Log File Auto Format"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&dwData,
|
|||
|
&dwSize);
|
|||
|
if (lStatus != ERROR_SUCCESS) {
|
|||
|
dwData = OPD_NAME_NNNNNN; // default is a serial number
|
|||
|
} else if (dwType != REG_DWORD) {
|
|||
|
dwData = OPD_NAME_NNNNNN; // default is a serial number
|
|||
|
} // else assume success
|
|||
|
|
|||
|
pArg->dwAutoNameFormat = dwData;
|
|||
|
|
|||
|
dwType = 0;
|
|||
|
dwData = 0;
|
|||
|
dwSize = sizeof(DWORD);
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Log File Type"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&dwData,
|
|||
|
&dwSize);
|
|||
|
if (lStatus != ERROR_SUCCESS) {
|
|||
|
dwData = OPD_CSV_FILE; // default is a CSV file
|
|||
|
} else if (dwType != REG_DWORD) {
|
|||
|
dwData = OPD_CSV_FILE; // default is a CSV file
|
|||
|
} // else assume success
|
|||
|
|
|||
|
// convert from OPD to PDH constant
|
|||
|
switch (dwData) {
|
|||
|
case OPD_TSV_FILE:
|
|||
|
pArg->dwLogType = PDH_LOG_TYPE_TSV;
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_BIN_FILE:
|
|||
|
pArg->dwLogType = PDH_LOG_TYPE_BINARY;
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_CSV_FILE:
|
|||
|
default:
|
|||
|
pArg->dwLogType = PDH_LOG_TYPE_CSV;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
dwType = 0;
|
|||
|
dwData = 0;
|
|||
|
dwSize = sizeof(DWORD);
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Sample Interval"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&dwData,
|
|||
|
&dwSize);
|
|||
|
if (lStatus != ERROR_SUCCESS) {
|
|||
|
dwData = SECONDS_IN_MINUTE; // default is 1 Minute samples
|
|||
|
} else if (dwType != REG_DWORD) {
|
|||
|
dwData = SECONDS_IN_MINUTE; // default is 1 Minute samples
|
|||
|
} // else assume success
|
|||
|
|
|||
|
pArg->dwTimeInterval = dwData;
|
|||
|
|
|||
|
// get filename or components if auto name
|
|||
|
|
|||
|
if (pArg->dwRenameIntervalCount > 0) {
|
|||
|
// this is an autoname file so get components
|
|||
|
dwType = 0;
|
|||
|
*szDefaultDir = 0;
|
|||
|
dwSize = MAX_PATH * sizeof(TCHAR);
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Log Default Directory"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&szDefaultDir[0],
|
|||
|
&dwSize);
|
|||
|
if (lStatus != ERROR_SUCCESS) {
|
|||
|
*szDefaultDir = 0;
|
|||
|
} // else assume success
|
|||
|
|
|||
|
dwType = 0;
|
|||
|
*szBaseName = 0;
|
|||
|
dwSize = MAX_PATH * sizeof(TCHAR);
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Base Filename"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&szBaseName[0],
|
|||
|
&dwSize);
|
|||
|
if (lStatus != ERROR_SUCCESS) {
|
|||
|
// apply default
|
|||
|
lstrcpy (szBaseName, TEXT("perfdata"));
|
|||
|
} // else assume success
|
|||
|
|
|||
|
dwType = 0;
|
|||
|
dwSize = 0;
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Command File"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
NULL,
|
|||
|
&dwSize);
|
|||
|
if (lStatus != ERROR_SUCCESS) {
|
|||
|
// assume no command filname
|
|||
|
pArg->szCmdFileName = NULL;
|
|||
|
} else {
|
|||
|
// allocate a buffer for this field and collect data
|
|||
|
pArg->szCmdFileName = (LPTSTR)G_ALLOC(dwSize);
|
|||
|
if (pArg->szCmdFileName != NULL) {
|
|||
|
// get command filename
|
|||
|
dwType = 0;
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Command File"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)pArg->szCmdFileName,
|
|||
|
&dwSize);
|
|||
|
if (lStatus != ERROR_SUCCESS) {
|
|||
|
// apply default
|
|||
|
pArg->szCmdFileName = NULL;
|
|||
|
} // else assume success
|
|||
|
}
|
|||
|
if (pArg->szCmdFileName == NULL) {
|
|||
|
// log error message
|
|||
|
// no command file could be read so issue
|
|||
|
// warning and continue
|
|||
|
szStringArray[0] = pArg->szQueryName;
|
|||
|
ReportEvent (hEventLog,
|
|||
|
EVENTLOG_WARNING_TYPE,
|
|||
|
0,
|
|||
|
PERFLOG_ALLOC_CMDFILE_BUFFER,
|
|||
|
NULL,
|
|||
|
1,
|
|||
|
sizeof(DWORD),
|
|||
|
szStringArray,
|
|||
|
(LPVOID)&lStatus);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
// this is a manual name file so read name
|
|||
|
dwType = 0;
|
|||
|
*szCurrentLogFile = 0;
|
|||
|
dwSize = MAX_PATH * sizeof(TCHAR);
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Log Filename"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&szCurrentLogFile[0],
|
|||
|
&dwSize);
|
|||
|
if (lStatus != ERROR_SUCCESS) {
|
|||
|
// apply default
|
|||
|
lstrcpy (szCurrentLogFile, TEXT("c:\\perfdata.log"));
|
|||
|
} // else assume success
|
|||
|
}
|
|||
|
|
|||
|
dwType = 0;
|
|||
|
dwData = 0;
|
|||
|
dwSize = sizeof(DWORD);
|
|||
|
lStatus = RegQueryValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Log File Serial Number"),
|
|||
|
NULL,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&dwData,
|
|||
|
&dwSize);
|
|||
|
if (lStatus != ERROR_SUCCESS) {
|
|||
|
dwData = 1; // default is to start at 1
|
|||
|
} else if (dwType != REG_DWORD) {
|
|||
|
dwData = 1; // default is to start at 1
|
|||
|
} // else assume success
|
|||
|
|
|||
|
pArg->dwCurrentSerialNumber = dwData;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
static
|
|||
|
LONG
|
|||
|
DoCommandFile (
|
|||
|
IN LPLOG_THREAD_DATA pArg,
|
|||
|
IN LPTSTR szLogFileName,
|
|||
|
IN BOOL bStillRunning
|
|||
|
)
|
|||
|
{
|
|||
|
LONG lStatus;
|
|||
|
BOOL bStatus;
|
|||
|
LPTSTR szCommandString;
|
|||
|
LONG nErrorMode;
|
|||
|
TCHAR TempBuffer [ 5 * MAX_PATH] ;
|
|||
|
DWORD StringLen;
|
|||
|
STARTUPINFO si;
|
|||
|
PROCESS_INFORMATION pi;
|
|||
|
DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS;
|
|||
|
|
|||
|
szCommandString = (LPTSTR)G_ALLOC(4096 * sizeof(TCHAR));
|
|||
|
|
|||
|
if (szCommandString != NULL) {
|
|||
|
// build command line arguments
|
|||
|
szCommandString[0] = _T('\"');
|
|||
|
lstrcpy (&szCommandString[1], szLogFileName);
|
|||
|
lstrcat (szCommandString, TEXT("\" "));
|
|||
|
lstrcat (szCommandString,
|
|||
|
(bStillRunning ? TEXT("1") : TEXT("0")));
|
|||
|
|
|||
|
// initialize Startup Info block
|
|||
|
memset (&si, 0, sizeof(si));
|
|||
|
si.cb = sizeof(si);
|
|||
|
si.dwFlags = STARTF_USESHOWWINDOW ;
|
|||
|
si.wShowWindow = SW_SHOWNOACTIVATE ;
|
|||
|
|
|||
|
memset (&pi, 0, sizeof(pi));
|
|||
|
|
|||
|
// supress pop-ups inf the detached process
|
|||
|
nErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
|||
|
|
|||
|
lstrcpy (TempBuffer, 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 (TempBuffer);
|
|||
|
if ((_tcsstr(TempBuffer, TEXT(".bat")) != NULL) ||
|
|||
|
(_tcsstr(TempBuffer, 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 (TempBuffer, pArg->szCmdFileName) ;
|
|||
|
StringLen = lstrlen (TempBuffer) ;
|
|||
|
|
|||
|
// now add on the alert text preceded with a space char
|
|||
|
TempBuffer [StringLen] = TEXT(' ') ;
|
|||
|
StringLen++ ;
|
|||
|
lstrcpy (&TempBuffer[StringLen], szCommandString) ;
|
|||
|
|
|||
|
bStatus = CreateProcess (
|
|||
|
NULL,
|
|||
|
TempBuffer,
|
|||
|
NULL, NULL, FALSE,
|
|||
|
dwCreationFlags,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&si,
|
|||
|
&pi);
|
|||
|
|
|||
|
SetErrorMode(nErrorMode);
|
|||
|
|
|||
|
if (bStatus) {
|
|||
|
lStatus = ERROR_SUCCESS;
|
|||
|
} else {
|
|||
|
lStatus = GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
lStatus = ERROR_OUTOFMEMORY;
|
|||
|
}
|
|||
|
|
|||
|
return lStatus;
|
|||
|
}
|
|||
|
|
|||
|
BOOL
|
|||
|
LoggingProc (
|
|||
|
IN LPLOG_THREAD_DATA pArg
|
|||
|
)
|
|||
|
{
|
|||
|
HQUERY hQuery;
|
|||
|
HCOUNTER hThisCounter;
|
|||
|
HLOG hLog;
|
|||
|
DWORD dwDelay;
|
|||
|
DWORD dwSampleInterval, dwSampleTime=-1;
|
|||
|
PDH_STATUS pdhStatus;
|
|||
|
DWORD dwNumCounters;
|
|||
|
LONG lStatus;
|
|||
|
TCHAR szDefaultDir[MAX_PATH];
|
|||
|
TCHAR szBaseName[MAX_PATH];
|
|||
|
|
|||
|
LPTSTR szThisPath;
|
|||
|
DWORD dwLogType = PDH_LOG_TYPE_CSV;
|
|||
|
BOOL bRun = FALSE;
|
|||
|
DWORD dwSamplesUntilNewFile;
|
|||
|
TCHAR szCurrentLogFile[MAX_PATH];
|
|||
|
LONG lWaitStatus;
|
|||
|
LPTSTR szStringArray[4];
|
|||
|
DWORD dwFileSizeLimit;
|
|||
|
LONGLONG llFileSizeLimit;
|
|||
|
LONGLONG llFileSize;
|
|||
|
PLOG_COUNTER_INFO pCtrInfo;
|
|||
|
|
|||
|
SYSTEMTIME st;
|
|||
|
LONGLONG llStartTime = 0;
|
|||
|
LONGLONG llFinishTime = 0;
|
|||
|
|
|||
|
// read registry values
|
|||
|
|
|||
|
if (!LoadDataFromRegistry (pArg, szDefaultDir, szBaseName, szCurrentLogFile)) {
|
|||
|
// unable to initialize the query from the registry
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// convert to milliseconds for use in timeouts
|
|||
|
dwSampleInterval = pArg->dwTimeInterval * 1000L;
|
|||
|
|
|||
|
// open query and add counters from info file
|
|||
|
|
|||
|
pdhStatus = PdhOpenQuery (NULL, 0, &hQuery); // from current activity
|
|||
|
if (pdhStatus == ERROR_SUCCESS) {
|
|||
|
dwNumCounters = 0;
|
|||
|
for (szThisPath = pArg->mszCounterList;
|
|||
|
*szThisPath != 0;
|
|||
|
szThisPath += lstrlen(szThisPath) + 1) {
|
|||
|
pdhStatus = PdhAddCounter (hQuery,
|
|||
|
(LPTSTR)szThisPath, dwNumCounters++, &hThisCounter);
|
|||
|
|
|||
|
if (pdhStatus == ERROR_SUCCESS) {
|
|||
|
// then add this handle to the list
|
|||
|
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 = pFirstCounter;
|
|||
|
pFirstCounter = pCtrInfo;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// to make sure we get to log the data
|
|||
|
SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
|||
|
|
|||
|
bRun = TRUE;
|
|||
|
while (bRun) {
|
|||
|
// Get the current Log filename
|
|||
|
if (pArg->dwRenameIntervalCount != 0) {
|
|||
|
// then this is an autonamed file
|
|||
|
// so make current name
|
|||
|
BuildCurrentLogFileName (
|
|||
|
szBaseName,
|
|||
|
szDefaultDir,
|
|||
|
szCurrentLogFile,
|
|||
|
&pArg->dwCurrentSerialNumber,
|
|||
|
pArg->dwAutoNameFormat,
|
|||
|
pArg->dwLogType);
|
|||
|
// reset loop counter
|
|||
|
switch (pArg->dwRenameIntervalUnits) {
|
|||
|
case OPD_RENAME_KBYTES:
|
|||
|
dwFileSizeLimit = pArg->dwRenameIntervalCount * 1024;
|
|||
|
dwSamplesUntilNewFile = 0;
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_RENAME_MBYTES:
|
|||
|
dwFileSizeLimit = pArg->dwRenameIntervalCount * 1024 * 1024;
|
|||
|
dwSamplesUntilNewFile = 0;
|
|||
|
break;
|
|||
|
|
|||
|
case OPD_RENAME_HOURS:
|
|||
|
case OPD_RENAME_DAYS:
|
|||
|
case OPD_RENAME_MONTHS:
|
|||
|
default:
|
|||
|
dwSamplesUntilNewFile = GetSamplesInRenameInterval(
|
|||
|
pArg->dwTimeInterval,
|
|||
|
pArg->dwRenameIntervalCount,
|
|||
|
pArg->dwRenameIntervalUnits);
|
|||
|
dwFileSizeLimit = 0;
|
|||
|
break;
|
|||
|
}
|
|||
|
} else {
|
|||
|
// filename is left as read from the registry
|
|||
|
dwSamplesUntilNewFile = 0;
|
|||
|
dwFileSizeLimit = 0;
|
|||
|
}
|
|||
|
llFileSizeLimit = dwFileSizeLimit;
|
|||
|
// open log file using this query
|
|||
|
dwLogType = pArg->dwLogType;
|
|||
|
pdhStatus = PdhOpenLog (
|
|||
|
szCurrentLogFile,
|
|||
|
PDH_LOG_WRITE_ACCESS |
|
|||
|
PDH_LOG_CREATE_ALWAYS,
|
|||
|
&dwLogType,
|
|||
|
hQuery,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
&hLog);
|
|||
|
|
|||
|
if (pdhStatus == ERROR_SUCCESS) {
|
|||
|
szStringArray[0] = pArg->szQueryName;
|
|||
|
szStringArray[1] = szCurrentLogFile;
|
|||
|
ReportEvent (hEventLog,
|
|||
|
EVENTLOG_INFORMATION_TYPE,
|
|||
|
0,
|
|||
|
PERFLOG_LOGGING_QUERY,
|
|||
|
NULL,
|
|||
|
2,
|
|||
|
0,
|
|||
|
szStringArray,
|
|||
|
NULL);
|
|||
|
// start sampling immediately
|
|||
|
dwDelay = 0;
|
|||
|
while ((lWaitStatus = WaitForSingleObject (pArg->hExitEvent, dwDelay)) == WAIT_TIMEOUT) {
|
|||
|
// the event flag will be set when the sampling should exit. if
|
|||
|
// the wait times out, then that means it's time to collect and
|
|||
|
// log another sample of data.
|
|||
|
// time the call to adjust the wait time
|
|||
|
|
|||
|
GetLocalFileTime (&st, &llStartTime);
|
|||
|
|
|||
|
pdhStatus = PdhUpdateLog (hLog, NULL);
|
|||
|
|
|||
|
if (pdhStatus == ERROR_SUCCESS) {
|
|||
|
// see if it's time to rename the file
|
|||
|
if (dwSamplesUntilNewFile) {
|
|||
|
if (!--dwSamplesUntilNewFile) break;
|
|||
|
} else if (llFileSizeLimit) {
|
|||
|
// see if the file is too big
|
|||
|
pdhStatus = PdhGetLogFileSize (hLog, &llFileSize);
|
|||
|
if (pdhStatus == ERROR_SUCCESS) {
|
|||
|
if (llFileSizeLimit <= llFileSize) break;
|
|||
|
}
|
|||
|
}
|
|||
|
// compute new timeout value
|
|||
|
if (dwSampleTime < dwSampleInterval) {
|
|||
|
dwDelay = dwSampleInterval - dwSampleTime;
|
|||
|
} else {
|
|||
|
dwDelay = 0;
|
|||
|
}
|
|||
|
} else {
|
|||
|
// unable to update the log so log event and exit
|
|||
|
ReportEvent (hEventLog,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
0,
|
|||
|
PERFLOG_UNABLE_UPDATE_LOG,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
sizeof(DWORD),
|
|||
|
NULL,
|
|||
|
(LPVOID)&pdhStatus);
|
|||
|
|
|||
|
bRun = FALSE;
|
|||
|
break;
|
|||
|
}
|
|||
|
GetLocalFileTime (&st, &llFinishTime);
|
|||
|
// compute difference and convert to milliseconds
|
|||
|
dwSampleTime = (DWORD)((llFinishTime - llStartTime) / 10000L);
|
|||
|
} // end while wait keeps timing out
|
|||
|
if (lWaitStatus == WAIT_OBJECT_0) {
|
|||
|
// then the loop was terminated by the Exit event
|
|||
|
// so clear the "run" flag to exit the loop & thread
|
|||
|
bRun = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// close log file, but keep query open
|
|||
|
PdhCloseLog (hLog, 0);
|
|||
|
|
|||
|
if (pArg->szCmdFileName != NULL) {
|
|||
|
DoCommandFile (pArg, szCurrentLogFile, bRun);
|
|||
|
}
|
|||
|
} else {
|
|||
|
// unable to open log file so log event log message
|
|||
|
szStringArray[0] = szCurrentLogFile;
|
|||
|
ReportEvent (hEventLog,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
0,
|
|||
|
PERFLOG_UNABLE_OPEN_LOG_FILE,
|
|||
|
NULL,
|
|||
|
1,
|
|||
|
sizeof(DWORD),
|
|||
|
szStringArray,
|
|||
|
(LPVOID)&pdhStatus);
|
|||
|
|
|||
|
bRun = FALSE; // exit now
|
|||
|
}
|
|||
|
} // end while (bRun)
|
|||
|
PdhCloseQuery (hQuery);
|
|||
|
|
|||
|
// update log serial number if necssary
|
|||
|
if (pArg->dwAutoNameFormat == OPD_NAME_NNNNNN) {
|
|||
|
lStatus = RegSetValueEx (
|
|||
|
pArg->hKeyQuery,
|
|||
|
TEXT("Log File Serial Number"),
|
|||
|
0L,
|
|||
|
REG_DWORD,
|
|||
|
(LPBYTE)&pArg->dwCurrentSerialNumber,
|
|||
|
sizeof(DWORD));
|
|||
|
}
|
|||
|
} else {
|
|||
|
// unable to open query so write event log message
|
|||
|
ReportEvent (hEventLog,
|
|||
|
EVENTLOG_ERROR_TYPE,
|
|||
|
0,
|
|||
|
PERFLOG_UNABLE_OPEN_LOG_QUERY,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
sizeof(DWORD),
|
|||
|
NULL,
|
|||
|
(LPVOID)&pdhStatus);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// free allocated buffers
|
|||
|
if (pArg->mszCounterList != NULL) {
|
|||
|
G_FREE(pArg->mszCounterList);
|
|||
|
pArg->mszCounterList = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (pArg->szCmdFileName != NULL) {
|
|||
|
G_FREE(pArg->szCmdFileName);
|
|||
|
pArg->szCmdFileName = NULL;
|
|||
|
}
|
|||
|
|
|||
|
return bRun;
|
|||
|
}
|
|||
|
|
|||
|
DWORD
|
|||
|
LoggingThreadProc (
|
|||
|
IN LPVOID lpThreadArg
|
|||
|
)
|
|||
|
{
|
|||
|
LPLOG_THREAD_DATA pThreadData = (LPLOG_THREAD_DATA)lpThreadArg;
|
|||
|
DWORD dwStatus = ERROR_SUCCESS;
|
|||
|
BOOL bContinue = TRUE;
|
|||
|
|
|||
|
if (pThreadData != NULL) {
|
|||
|
// read config from registry
|
|||
|
|
|||
|
do {
|
|||
|
// read config from registry
|
|||
|
// expand counter paths as necessary
|
|||
|
// call Logging function
|
|||
|
bContinue = LoggingProc (pThreadData);
|
|||
|
// see if this thread was paused for reloading
|
|||
|
// or stopped to terminate
|
|||
|
if (pThreadData->bReloadNewConfig) {
|
|||
|
bContinue = TRUE;
|
|||
|
} // else bContinue is always returned as FALSE
|
|||
|
// so that will terminate this loop
|
|||
|
} while (bContinue);
|
|||
|
dwStatus = ERROR_SUCCESS;
|
|||
|
} else {
|
|||
|
// unable to find data block so return
|
|||
|
dwStatus = ERROR_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
return dwStatus;
|
|||
|
}
|