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