/*++ 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 // For Trace *** - these are only necessary because of union query data struct. #include #include #include #include #include #include #if _IMPLEMENT_WMI #include #include #endif #include #include // for net message function #include #include #include #include #include #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; }