|
|
/*++
Copyright (C) 1998-1999 Microsoft Corporation
Module Name:
smlogs.cpp
Abstract:
Implementation of the base class representing the Performance Logs and Alerts service.
--*/
#include "Stdafx.h"
// Define the following to use the minimum of shlwapip.h
#ifndef NO_SHLWAPI_PATH
#define NO_SHLWAPI_PATH
#endif
#ifndef NO_SHLWAPI_REG
#define NO_SHLWAPI_REG
#endif
#ifndef NO_SHLWAPI_UALSTR
#define NO_SHLWAPI_UALSTR
#endif
#ifndef NO_SHLWAPI_STREAM
#define NO_SHLWAPI_STREAM
#endif
#ifndef NO_SHLWAPI_HTTP
#define NO_SHLWAPI_HTTP
#endif
#ifndef NO_SHLWAPI_INTERNAL
#define NO_SHLWAPI_INTERNAL
#endif
#ifndef NO_SHLWAPI_GDI
#define NO_SHLWAPI_GDI
#endif
#ifndef NO_SHLWAPI_UNITHUNK
#define NO_SHLWAPI_UNITHUNK
#endif
#ifndef NO_SHLWAPI_TPS
#define NO_SHLWAPI_TPS
#endif
#ifndef NO_SHLWAPI_MLUI
#define NO_SHLWAPI_MLUI
#endif
#include <shlwapi.h> // For SHLoadIndirectString
#include <shlwapip.h> // For SHLoadIndirectString
#include <pdh.h> // For MIN_TIME_VALUE, MAX_TIME_VALUE
#include <pdhp.h> // For pdhi methods
#include <wbemidl.h>
#include "smlogres.h"
#include "smcfgmsg.h"
#include "smalrtq.h"
#include "smctrqry.h"
#include "smtraceq.h"
#include "smlogs.h"
#include "strnoloc.h"
USE_HANDLE_MACROS("SMLOGCFG(smlogs.cpp)");
#define DEFAULT_LOG_FILE_FOLDER L"%SystemDrive%\\PerfLogs"
//
// Constructor
CSmLogService::CSmLogService() : m_hKeyMachine ( NULL ), m_hKeyLogService ( NULL ), m_hKeyLogServiceRoot ( NULL ), m_bIsOpen ( FALSE ), m_bReadOnly ( FALSE ) { // String allocation errors are thrown, to be
// captured by rootnode alloc exception handler
m_QueryList.RemoveAll(); // initialize the list
ZeroMemory(&m_OSVersion, sizeof(m_OSVersion)); return; }
//
// Destructor
CSmLogService::~CSmLogService() { // make sure Close method was called first!
ASSERT ( NULL == m_QueryList.GetHeadPosition() ); ASSERT ( NULL == m_hKeyMachine ); ASSERT ( NULL == m_hKeyLogService ); ASSERT ( NULL == m_hKeyLogServiceRoot ); return; }
PSLQUERY CSmLogService::CreateTypedQuery ( const CString& rstrName, DWORD dwLogType ) { HKEY hKeyQuery; PSLQUERY pNewLogQuery = NULL; DWORD dwStatus = ERROR_SUCCESS; DWORD dwDisposition; DWORD dwRegValue; UUID uuidNew; RPC_STATUS rpcStat = RPC_S_OK; LPTSTR pszUuid = NULL; INT iBufLen = rstrName.GetLength()+1; LPTSTR pszName = NULL; LPTSTR pStat = NULL; BOOL bDupFound = FALSE; CString strNewQueryName; CString strCollectionName;
if (m_bReadOnly) { SetLastError (SMCFG_NO_MODIFY_ACCESS); return NULL; // unable to create without WRITE access
} else { // Initialize to success status.
SetLastError( dwStatus ); }
if ( !IsWindows2000Server() ) { // For servers later than Windows2000, use a GUID as the key name for a query.
rpcStat = UuidCreate( &uuidNew );
if ( RPC_S_OK != rpcStat && RPC_S_UUID_LOCAL_ONLY != rpcStat ) { rpcStat = UuidCreateSequential ( &uuidNew ); }
if ( RPC_S_OK == rpcStat || RPC_S_UUID_LOCAL_ONLY == rpcStat ) { rpcStat = UuidToString ( &uuidNew, &pszUuid );
if ( RPC_S_OK == rpcStat ) {
ASSERT ( NULL != pszUuid );
MFC_TRY strNewQueryName.Format ( L"{%s}", pszUuid ); MFC_CATCH_DWSTATUS
RpcStringFree ( &pszUuid ); } else { dwStatus = rpcStat; } } // RPC_STATUS values in rpcnterr.h correspond to appropriate values.
dwStatus = rpcStat; } else { // For Windows 2000, use query name as registry key name.
MFC_TRY strNewQueryName = rstrName; MFC_CATCH_DWSTATUS
}
if ( ERROR_SUCCESS == dwStatus ) {
// Query key name created
// Create the query specified, checking for duplicate query by key name.
dwStatus = RegCreateKeyExW ( m_hKeyLogService, strNewQueryName, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hKeyQuery, &dwDisposition);
if ( REG_OPENED_EXISTING_KEY == dwDisposition ) { dwStatus = SMCFG_DUP_QUERY_NAME; } }
if ( ERROR_SUCCESS == dwStatus ) {
// Initialize the current state value. After it is
// initialized, it is only modified when:
// 1) Set to Stopped or Started by the service
// 2) Set to Start Pending by the config snapin.
dwRegValue = SLQ_QUERY_STOPPED;
dwStatus = RegSetValueEx ( hKeyQuery, L"Current State", 0L, REG_DWORD, (CONST BYTE *)&dwRegValue, sizeof(DWORD));
if ( ERROR_SUCCESS == dwStatus ) { // Initialize the log type to "new" to indicate partially created logs
dwRegValue = SLQ_NEW_LOG;
dwStatus = RegSetValueEx ( hKeyQuery, L"Log Type", 0L, REG_DWORD, (CONST BYTE *)&dwRegValue, sizeof(DWORD)); }
if ( ERROR_SUCCESS == dwStatus && !IsWindows2000Server() ) { // Initialize the collection name for post Windows 2000 systems
MFC_TRY strCollectionName = rstrName; MFC_CATCH_DWSTATUS
if ( ERROR_SUCCESS == dwStatus ) { dwStatus = RegSetValueEx ( hKeyQuery, L"Collection Name", 0L, REG_SZ, (CONST BYTE *)strCollectionName.GetBufferSetLength( strCollectionName.GetLength() ), strCollectionName.GetLength()*sizeof(TCHAR) );
strCollectionName.ReleaseBuffer(); }
// For post Windows 2000 counters, search for duplicate by collection name.
if ( ERROR_SUCCESS == dwStatus ) { dwStatus = FindDuplicateQuery ( rstrName, bDupFound ); } } if ( ERROR_SUCCESS == dwStatus && !bDupFound ) { // create a new object and add it to the query list
dwStatus = LoadSingleQuery ( &pNewLogQuery, dwLogType, rstrName, strNewQueryName, hKeyQuery, TRUE ); } else { if ( bDupFound ) { dwStatus = SMCFG_DUP_QUERY_NAME; } } }
if ( ERROR_SUCCESS != dwStatus ) { // Delete also closes the registry key hKeyQuery.
if ( !strNewQueryName.IsEmpty() ) { RegDeleteKeyW ( m_hKeyLogService, strNewQueryName ); SetLastError ( dwStatus ); } }
return pNewLogQuery; }
DWORD CSmLogService::UnloadSingleQuery (PSLQUERY pQuery) { DWORD dwStatus = ERROR_SUCCESS; PSLQUERY pLogQuery = NULL; POSITION listPos = NULL; BOOL bFoundEntry = FALSE;
// find matching entry
if (!m_QueryList.IsEmpty()) { listPos = m_QueryList.Find (pQuery, NULL); if ( NULL != listPos ) { pLogQuery = m_QueryList.GetAt(listPos); bFoundEntry = TRUE; } }
if (bFoundEntry) { ASSERT ( NULL != listPos );
// remove from list
m_QueryList.RemoveAt (listPos); pLogQuery->Close(); delete pLogQuery; } else { // not found
dwStatus = ERROR_FILE_NOT_FOUND; }
return dwStatus; }
DWORD CSmLogService::DeleteQuery ( PSLQUERY pQuery ) { PSLQUERY pLogQuery = NULL; DWORD dwStatus = ERROR_SUCCESS; POSITION listPos = NULL; BOOL bFoundEntry = FALSE; CString strLogKeyName;
if (m_bReadOnly) { dwStatus = ERROR_ACCESS_DENIED; } else { // find matching entry
if (!m_QueryList.IsEmpty()) { listPos = m_QueryList.Find (pQuery, NULL); if (listPos != NULL) { pLogQuery = m_QueryList.GetAt(listPos); bFoundEntry = TRUE; } } if (bFoundEntry) { ASSERT (listPos != NULL); MFC_TRY pLogQuery->GetLogKeyName( strLogKeyName ); MFC_CATCH_DWSTATUS;
if ( ERROR_SUCCESS == dwStatus ) { // remove from list
m_QueryList.RemoveAt (listPos); pLogQuery->Close();
// Delete in the registry
RegDeleteKeyW ( m_hKeyLogService, strLogKeyName ); delete pLogQuery; } } else { // not found
dwStatus = ERROR_FILE_NOT_FOUND; } } return dwStatus; }
DWORD CSmLogService::DeleteQuery ( const CString& rstrName ) { PSLQUERY pLogQuery = NULL; DWORD dwStatus = ERROR_SUCCESS; POSITION listPos; BOOL bFoundEntry = FALSE;
if (m_bReadOnly) { dwStatus = ERROR_ACCESS_DENIED; } else { // find matching entry
if (!m_QueryList.IsEmpty()) { listPos = m_QueryList.GetHeadPosition(); while (listPos != NULL) { pLogQuery = m_QueryList.GetNext(listPos); if ( 0 == rstrName.CompareNoCase ( pLogQuery->GetLogName() ) ) { // match found so bail here
bFoundEntry = TRUE; break; } } } if (bFoundEntry) { dwStatus = DeleteQuery ( pLogQuery ); } else { // not found
dwStatus = ERROR_FILE_NOT_FOUND; } } return dwStatus; }
DWORD CSmLogService::LoadDefaultLogFileFolder ( void ) { DWORD dwStatus = ERROR_SUCCESS; LPWSTR szLocalPath = NULL; LPWSTR szExpanded = NULL; INT cchLen; INT cchExpandedLen; DWORD dwBufferSize = MAX_PATH + 1;
m_strDefaultLogFileFolder.Empty();
MFC_TRY szLocalPath = new WCHAR [ dwBufferSize ];
dwBufferSize *= sizeof(WCHAR);
if ( NULL != m_hKeyLogServiceRoot ) {
dwStatus = RegQueryValueExW ( m_hKeyLogServiceRoot, (LPCTSTR)L"DefaultLogFileFolder", NULL, 0L, (LPBYTE)szLocalPath, &dwBufferSize);
//
// No message on error. If error, just load the default.
//
if ( sizeof(WCHAR) < dwBufferSize ) { if ( IsLocalMachine() ) { cchLen = 0; cchExpandedLen = 0;
cchLen = ExpandEnvironmentStrings ( szLocalPath, NULL, 0 );
if ( 0 < cchLen ) { szExpanded = new WCHAR[cchLen]; cchExpandedLen = ExpandEnvironmentStrings ( szLocalPath, szExpanded, cchLen );
if ( 0 < cchExpandedLen && MAX_PATH > cchExpandedLen ) { m_strDefaultLogFileFolder = szExpanded; } else { dwStatus = GetLastError(); m_strDefaultLogFileFolder.Empty(); } } else { dwStatus = GetLastError(); } } else { m_strDefaultLogFileFolder = szLocalPath; } }
if ( sizeof(WCHAR) >= dwBufferSize || m_strDefaultLogFileFolder.IsEmpty() ) { ResourceStateManager rsm; CString strFolderName; strFolderName.LoadString ( IDS_DEFAULT_LOG_FILE_FOLDER );
if ( dwBufferSize > (MAX_PATH + 1)*sizeof(WCHAR) ) { if ( NULL != szLocalPath ) { delete [] szLocalPath; szLocalPath = NULL; } szLocalPath = new WCHAR [strFolderName.GetLength() + 1]; }
lstrcpy ( szLocalPath, strFolderName );
//
// Exact copy of code for processing the registry setting.
//
if ( IsLocalMachine() ) { cchLen = 0; cchExpandedLen = 0;
cchLen = ExpandEnvironmentStrings ( szLocalPath, NULL, 0 );
if ( 0 < cchLen ) { szExpanded = new WCHAR[cchLen]; cchExpandedLen = ExpandEnvironmentStrings ( szLocalPath, szExpanded, cchLen );
if ( 0 < cchExpandedLen && MAX_PATH > cchExpandedLen ) { m_strDefaultLogFileFolder = szExpanded; } else { dwStatus = GetLastError(); m_strDefaultLogFileFolder.Empty(); } } else { dwStatus = GetLastError(); } } else { m_strDefaultLogFileFolder = szLocalPath; } } } MFC_CATCH_DWSTATUS
if ( NULL != szLocalPath ) { delete [] szLocalPath; }
if ( NULL != szExpanded ) { delete [] szExpanded; }
return dwStatus; }
DWORD CSmLogService::LoadSingleQuery ( PSLQUERY* ppQuery, DWORD dwLogType, const CString& rstrName, const CString& rstrLogKeyName, HKEY hKeyQuery, BOOL bNew ) { DWORD dwStatus = ERROR_SUCCESS; PSLQUERY pNewQuery = NULL;
if ( NULL != ppQuery ) { *ppQuery = NULL;
// create a new query object and add it to the query list
MFC_TRY if ( SLQ_COUNTER_LOG == dwLogType ) { pNewQuery = new SLCTRQUERY ( this ); } else if ( SLQ_TRACE_LOG == dwLogType ) { pNewQuery = new SLTRACEQUERY ( this ); } else if ( SLQ_ALERT == dwLogType ) { pNewQuery = new SLALERTQUERY ( this ); } MFC_CATCH_DWSTATUS
if ( ERROR_SUCCESS == dwStatus && NULL != pNewQuery ) { pNewQuery->SetNew ( bNew );
dwStatus = pNewQuery->Open( rstrName, hKeyQuery, m_bReadOnly );
if ( ERROR_SUCCESS == dwStatus ) { dwStatus = pNewQuery->SetLogKeyName ( rstrLogKeyName ); }
if ( ERROR_SUCCESS == dwStatus ) { // then add it to the list
MFC_TRY m_QueryList.AddHead ( pNewQuery ); MFC_CATCH_DWSTATUS if ( ERROR_SUCCESS != dwStatus ) { // close this query object
pNewQuery->Close(); } }
if ( ERROR_SUCCESS != dwStatus ) { // delete this query object
delete pNewQuery; } }
if ( ERROR_SUCCESS == dwStatus ) { *ppQuery = pNewQuery; } } else { dwStatus = ERROR_INVALID_PARAMETER; } return dwStatus; }
DWORD CSmLogService::LoadQueries ( DWORD dwLogType ) { DWORD dwStatus = ERROR_SUCCESS; DWORD dwQueryIndex = 0; LONG lEnumStatus = ERROR_SUCCESS; WCHAR szQueryKeyName[MAX_PATH + 1]; DWORD dwQueryKeyNameLen; LPWSTR szCollectionName = NULL; UINT uiCollectionNameLen = 0; FILETIME ftLastWritten; HKEY hKeyQuery; PSLQUERY pNewLogQuery = NULL; DWORD dwType = 0; DWORD dwBufferSize = sizeof(DWORD); DWORD dwRegValue; CString strQueryName;
// Load all queries for the specified registry key.
// Enumerate the log names and create a new log object
// for each one found.
dwQueryKeyNameLen = sizeof ( szQueryKeyName ) / sizeof ( WCHAR ); memset (szQueryKeyName, 0, sizeof (szQueryKeyName));
while ( ERROR_SUCCESS == ( lEnumStatus = RegEnumKeyExW ( m_hKeyLogService, dwQueryIndex, szQueryKeyName, &dwQueryKeyNameLen, NULL, NULL, NULL, &ftLastWritten ) ) ) {
// open the query specified
dwStatus = RegOpenKeyExW ( m_hKeyLogService, szQueryKeyName, 0, (m_bReadOnly ? KEY_READ : KEY_READ | KEY_WRITE), &hKeyQuery); if ( ERROR_SUCCESS == dwStatus ) { // create a new object and add it to the query list
// Determine the log type.
dwType = 0; dwStatus = RegQueryValueExW ( hKeyQuery, L"Log Type", NULL, &dwType, (LPBYTE)&dwRegValue, &dwBufferSize ); if ( ( ERROR_SUCCESS == dwStatus ) && ( dwLogType == dwRegValue ) ) {
dwStatus = SmReadRegistryIndirectStringValue ( hKeyQuery, L"Collection Name", NULL, &szCollectionName, &uiCollectionNameLen ); MFC_TRY if ( ERROR_SUCCESS == dwStatus && NULL != szCollectionName ) { if ( 0 < lstrlen ( szCollectionName ) ) { strQueryName = szCollectionName; } else { strQueryName = szQueryKeyName; dwStatus = ERROR_SUCCESS; } } else { strQueryName = szQueryKeyName; dwStatus = ERROR_SUCCESS; } MFC_CATCH_DWSTATUS;
if ( NULL != szCollectionName ) { G_FREE ( szCollectionName ); szCollectionName = NULL; uiCollectionNameLen = 0; }
if ( ERROR_SUCCESS == dwStatus ) { dwStatus = LoadSingleQuery ( &pNewLogQuery, dwRegValue, strQueryName, szQueryKeyName, hKeyQuery, FALSE );
if ( ERROR_SUCCESS != dwStatus ) { // Todo: Error message
dwStatus = ERROR_SUCCESS; } }
} else { // Try the next item in the list
RegCloseKey (hKeyQuery); dwStatus = ERROR_SUCCESS; } } // set up for the next item in the list
dwQueryKeyNameLen = sizeof (szQueryKeyName) / sizeof (szQueryKeyName[0]); memset (szQueryKeyName, 0, sizeof (szQueryKeyName)); dwQueryIndex++; } return dwStatus; }
//
// Open function. Opens all existing log query entries.
//
DWORD CSmLogService::Open ( const CString& rstrMachineName) { DWORD dwStatus = ERROR_SUCCESS;
// Initialize strings
SetMachineName ( rstrMachineName ); SetDisplayName ( m_strBaseName );
if ( rstrMachineName.IsEmpty() ) { m_hKeyMachine = HKEY_LOCAL_MACHINE; } else { dwStatus = RegConnectRegistryW ( rstrMachineName, HKEY_LOCAL_MACHINE, &m_hKeyMachine); }
if (dwStatus == ERROR_SUCCESS) {
// open a read-only key to the registry root key for this service, to obtain
// root-level values.
dwStatus = RegOpenKeyExW ( m_hKeyMachine, (LPCWSTR)L"SYSTEM\\CurrentControlSet\\Services\\SysmonLog", 0, KEY_READ, &m_hKeyLogServiceRoot); // No message on failure. Currently only affects default log file folder name.
// open a key to the registry log queries key for this service
dwStatus = RegOpenKeyExW ( m_hKeyMachine, (LPCWSTR)L"SYSTEM\\CurrentControlSet\\Services\\SysmonLog\\Log Queries", 0, KEY_READ | KEY_WRITE, &m_hKeyLogService);
if (dwStatus != ERROR_SUCCESS) { // unable to access the key for write access, so try read only
dwStatus = RegOpenKeyExW ( m_hKeyMachine, (LPCWSTR)L"SYSTEM\\CurrentControlSet\\Services\\SysmonLog\\Log Queries", 0, KEY_READ, &m_hKeyLogService); if (dwStatus != ERROR_SUCCESS) { // unable to open the key for read access so bail out
// assume the service has not been installed
// (though we should probably check first to make sure)
m_hKeyLogService = NULL; if ( ERROR_ACCESS_DENIED == dwStatus ) { dwStatus = SMCFG_NO_READ_ACCESS; } } else { // opened for read access so set the flag
m_bReadOnly = TRUE; } } }
// Install the service if necessary.
if ( ( dwStatus != SMCFG_NO_READ_ACCESS ) ) { dwStatus = Install( rstrMachineName ); } // Load all queries
if ( ( dwStatus == ERROR_SUCCESS ) && ( NULL != m_hKeyLogService ) ) { dwStatus = LoadQueries(); }
if ( ERROR_SUCCESS == dwStatus ) { SetOpen ( TRUE ); }
return dwStatus; }
DWORD CSmLogService::UnloadQueries () { DWORD dwStatus = ERROR_SUCCESS; PSLQUERY pQuery = NULL; POSITION Pos = m_QueryList.GetHeadPosition();
// Ensure that all property dialogs are closed before unloading queries.
while ( Pos != NULL) { pQuery = m_QueryList.GetNext( Pos ); if ( NULL != pQuery->GetActivePropertySheet() ) { // Todo: Server Beta 3 - Need specific error code
dwStatus = IDS_ERRMSG_DELETE_OPEN_QUERY; break; } }
if ( ERROR_SUCCESS == dwStatus ) { Pos = m_QueryList.GetHeadPosition();
// Update each query in this service by walking down the list.
while ( Pos != NULL) { pQuery = m_QueryList.GetNext( Pos ); pQuery->Close(); delete (pQuery); } // Empty the list now that everything has been closed;
m_QueryList.RemoveAll(); } return dwStatus; }
//
// Close Function
// closes registry handles and frees allocated memory
//
DWORD CSmLogService::Close () {
LOCALTRACE (L"Closing SysmonLog Service Object\n");
UnloadQueries();
// close any open registry keys
if (m_hKeyMachine != NULL) { RegCloseKey (m_hKeyMachine); m_hKeyMachine = NULL; }
if (m_hKeyLogService != NULL) { RegCloseKey (m_hKeyLogService); m_hKeyLogService = NULL; }
if (m_hKeyLogServiceRoot!= NULL) { RegCloseKey (m_hKeyLogServiceRoot); m_hKeyLogServiceRoot = NULL; }
SetOpen ( FALSE );
return ERROR_SUCCESS; }
DWORD CSmLogService::UpdateConfig() { // If any queries are (newly) set to auto start, then set the
// service to auto start. Otherwise, set to manual start.
// When setting to auto start, also set failure mode to restart
DWORD dwStatus = ERROR_SUCCESS; BOOL bStatus = 0; POSITION listPos; PSLQUERY pLogQuery = NULL; SC_HANDLE hSC = NULL; SC_HANDLE hService = NULL; BOOL bAutoStart = FALSE; DWORD pqsConfigBuff[128]; QUERY_SERVICE_CONFIG* pqsConfig; SC_ACTION* parrSingleFailAction = NULL; SERVICE_FAILURE_ACTIONS structFailActions; DWORD dwMoreBytes = 0; BOOL bUpdate = FALSE;
// check for duplicate entry
if (!m_QueryList.IsEmpty()) {
listPos = m_QueryList.GetHeadPosition(); while (listPos != NULL) { pLogQuery = m_QueryList.GetNext(listPos); if ( pLogQuery->IsAutoStart() ) { bAutoStart = TRUE; break; } } }
// open SC database
hSC = OpenSCManager ( GetMachineName(), NULL, GENERIC_READ );
if (hSC != NULL) { // open service
hService = OpenService ( hSC, TEXT("SysmonLog"), SERVICE_CHANGE_CONFIG | SERVICE_QUERY_CONFIG | SERVICE_START );
if (hService != NULL) { // get current config
memset (pqsConfigBuff, 0, sizeof(pqsConfigBuff)); pqsConfig = (QUERY_SERVICE_CONFIG*)pqsConfigBuff;
if ( QueryServiceConfig ( hService, pqsConfig, sizeof(pqsConfigBuff), &dwMoreBytes)) { // See if the current status is different
// from the selection. If it is, then change
// the current mode.
if ( bAutoStart ) { if ( SERVICE_DEMAND_START == pqsConfig->dwStartType ) { bUpdate = TRUE; } } else { // Manual start selected
if ( SERVICE_AUTO_START == pqsConfig->dwStartType ) { bUpdate = TRUE; } } } else { // else unable to read the current status so update anyway
bUpdate = TRUE; }
if ( bUpdate ) { MFC_TRY parrSingleFailAction = new SC_ACTION[3]; MFC_CATCH_DWSTATUS;
if ( NULL != parrSingleFailAction ) { parrSingleFailAction[0].Delay = eRestartDelayMilliseconds; parrSingleFailAction[1].Delay = eRestartDelayMilliseconds; parrSingleFailAction[2].Delay = eRestartDelayMilliseconds;
if ( bAutoStart ) { parrSingleFailAction[0].Type = SC_ACTION_RESTART; parrSingleFailAction[1].Type = SC_ACTION_RESTART; parrSingleFailAction[2].Type = SC_ACTION_RESTART; } else { parrSingleFailAction[0].Type = SC_ACTION_NONE; parrSingleFailAction[1].Type = SC_ACTION_NONE; parrSingleFailAction[2].Type = SC_ACTION_NONE; }
structFailActions.dwResetPeriod = eResetDelaySeconds; structFailActions.lpRebootMsg = NULL; structFailActions.lpCommand = NULL; structFailActions.cActions = 3; structFailActions.lpsaActions = parrSingleFailAction;
bStatus = ChangeServiceConfig ( hService, SERVICE_NO_CHANGE, (bAutoStart ? SERVICE_AUTO_START : SERVICE_DEMAND_START), SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
if ( 0 == bStatus ) { dwStatus = GetLastError(); } else { bStatus = ChangeServiceConfig2 ( hService, SERVICE_CONFIG_FAILURE_ACTIONS, &structFailActions ); if ( 0 == bStatus ) { dwStatus = GetLastError(); } } delete parrSingleFailAction;
} else { dwStatus = ERROR_OUTOFMEMORY; } }
CloseServiceHandle (hService);
} else { dwStatus = GetLastError(); ASSERT (dwStatus != 0); }
CloseServiceHandle (hSC);
} else { dwStatus = GetLastError(); } // OpenSCManager
return dwStatus; }
//
// SyncWithRegistry()
// reads the current values for all queries from the registry
// and reloads the internal values to match
//
DWORD CSmLogService::SyncWithRegistry ( void ) { DWORD dwStatus = ERROR_SUCCESS; CString strDesc; ResourceStateManager rsm;
// Unload queries and reload, to capture new queries.
// This is necessary for monitoring remote systems,
// and if multiple users are active on the same system.
dwStatus = UnloadQueries ();
if ( ERROR_SUCCESS == dwStatus ) { dwStatus = LoadQueries (); } return dwStatus; }
DWORD CSmLogService::GetState( void ) { // Check the status of the log service via the service controller.
// 0 returned in case of error.
LONG dwStatus = ERROR_SUCCESS; DWORD dwState = 0; // Error by default.
SERVICE_STATUS ssData; SC_HANDLE hSC; SC_HANDLE hLogService;
// open SC database
hSC = OpenSCManager ( GetMachineName(), NULL, SC_MANAGER_CONNECT);
if (hSC != NULL) { // open service
hLogService = OpenService ( hSC, TEXT("SysmonLog"), SERVICE_INTERROGATE ); if (hLogService != NULL) { if ( ControlService ( hLogService, SERVICE_CONTROL_INTERROGATE, &ssData)) {
dwState = ssData.dwCurrentState; } else { dwStatus = GetLastError(); dwState = SERVICE_STOPPED; }
CloseServiceHandle (hLogService); } else { dwStatus = GetLastError(); }
CloseServiceHandle (hSC); } else { dwStatus = GetLastError(); } // OpenSCManager
return dwState; }
BOOL CSmLogService::IsRunning( void ) { DWORD dwState = GetState(); BOOL bRunning = FALSE;
if ( 0 != dwState && SERVICE_STOPPED != dwState && SERVICE_STOP_PENDING != dwState ) { bRunning = TRUE; } return bRunning; }
DWORD CSmLogService::CreateDefaultLogQueries( void ) { DWORD dwStatus = ERROR_SUCCESS; PSLQUERY pQuery = NULL; CString strTemp; CString strModuleName; BOOL bRegistryUpdated; BOOL bDupFound = FALSE;
ResourceStateManager rsm;
// Creates default "System Overview" counter log query
MFC_TRY strTemp.LoadString ( IDS_DEFAULT_CTRLOG_QUERY_NAME ); MFC_CATCH_DWSTATUS;
if ( ERROR_SUCCESS == dwStatus ) { pQuery = CreateTypedQuery ( strTemp, SLQ_COUNTER_LOG );
if ( NULL != pQuery && !IsWindows2000Server() ) { // Default query collection name is stored as MUI indirect string after Windows 2000
MFC_TRY ::GetModuleFileName( AfxGetInstanceHandle(), strModuleName.GetBufferSetLength(MAX_PATH), MAX_PATH );
strTemp.Format (_T("@%s,-%d"), strModuleName, IDS_DEFAULT_CTRLOG_QUERY_NAME ); strModuleName.ReleaseBuffer();
MFC_CATCH_DWSTATUS;
if ( ERROR_SUCCESS == dwStatus ) { dwStatus = RegSetValueEx ( pQuery->GetQueryKey(), L"Collection Name Indirect", 0L, REG_SZ, (CONST BYTE *)strTemp.GetBufferSetLength( strTemp.GetLength() ), strTemp.GetLength()*sizeof(WCHAR) );
strTemp.ReleaseBuffer(); } // CreateTypedQuery checks for the existence of the default query
// using the the query name.
// Check for the existence of the default query under the MUI indirect
// name as well.
if ( NULL != pQuery ) { if ( ERROR_SUCCESS == dwStatus ) { FindDuplicateQuery ( strTemp, bDupFound ); if ( bDupFound ) { DeleteQuery ( pQuery ); pQuery = NULL; dwStatus = ERROR_SUCCESS; } } } }
if ( NULL != pQuery ) { SLQ_TIME_INFO slqTime; PSLCTRQUERY pCtrQuery = NULL;
MFC_TRY pCtrQuery = pQuery->CastToCounterLogQuery(); pCtrQuery->SetFileNameAutoFormat ( SLF_NAME_NONE ); pCtrQuery->SetLogFileType ( SLF_BIN_FILE ); pCtrQuery->SetDataStoreAppendMode ( SLF_DATA_STORE_OVERWRITE );
strTemp.LoadString ( IDS_DEFAULT_CTRLOG_COMMENT ); pCtrQuery->SetLogComment ( strTemp ); if ( !IsWindows2000Server() ) { strTemp.Format (_T("@%s,-%d"), strModuleName, IDS_DEFAULT_CTRLOG_COMMENT ); pCtrQuery->SetLogCommentIndirect ( strTemp ); }
strTemp.LoadString ( IDS_DEFAULT_CTRLOG_FILE_NAME ); pCtrQuery->SetLogFileName ( strTemp );
if ( !IsWindows2000Server() ) { strTemp.Format (_T("@%s,-%d"), strModuleName, IDS_DEFAULT_CTRLOG_FILE_NAME ); pCtrQuery->SetLogFileNameIndirect ( strTemp ); }
pCtrQuery->AddCounter ( CGlobalString::m_cszDefaultCtrLogCpuPath ); pCtrQuery->AddCounter ( CGlobalString::m_cszDefaultCtrLogMemoryPath ); pCtrQuery->AddCounter ( CGlobalString::m_cszDefaultCtrLogDiskPath );
// Start mode and time
memset (&slqTime, 0, sizeof(slqTime)); slqTime.wTimeType = SLQ_TT_TTYPE_START; slqTime.wDataType = SLQ_TT_DTYPE_DATETIME; slqTime.dwAutoMode = SLQ_AUTO_MODE_NONE; slqTime.llDateTime = MAX_TIME_VALUE;
pCtrQuery->SetLogTime (&slqTime, (DWORD)slqTime.wTimeType);
// Stop mode and time
slqTime.wTimeType = SLQ_TT_TTYPE_STOP; slqTime.llDateTime = MIN_TIME_VALUE;
pCtrQuery->SetLogTime (&slqTime, (DWORD)slqTime.wTimeType);
pCtrQuery->UpdateService( bRegistryUpdated );
// Set the default log to Execute Only.
dwStatus = pCtrQuery->UpdateExecuteOnly ();
MFC_CATCH_DWSTATUS if ( ERROR_SUCCESS == dwStatus && NULL != pCtrQuery ) { VERIFY ( ERROR_SUCCESS == UnloadSingleQuery ( pCtrQuery ) ); } else if ( NULL != pCtrQuery ) { DeleteQuery ( pCtrQuery ); } } else { dwStatus = GetLastError();
if ( SMCFG_DUP_QUERY_NAME == dwStatus ) { dwStatus = ERROR_SUCCESS; } } } return dwStatus; }
DWORD CSmLogService::Install ( const CString& rstrMachineName ) { DWORD dwStatus = ERROR_SUCCESS; DWORD dwDisposition = 0; HKEY hKeyPerfLog = NULL; CString strValueName; DWORD dwType; DWORD dwRegValue; DWORD dwBufferSize; BOOL bReadOnlyPerfLogKey = FALSE; BOOL bReadOnlyLogQueriesKey = FALSE;
ResourceStateManager rsm;
//
// Get machine OS version
//
PdhiPlaGetVersion( rstrMachineName, &m_OSVersion );
if ( NULL == m_hKeyMachine ) { if ( rstrMachineName.IsEmpty() ) { m_hKeyMachine = HKEY_LOCAL_MACHINE; } else { dwStatus = RegConnectRegistryW ( rstrMachineName, HKEY_LOCAL_MACHINE, &m_hKeyMachine); } }
if ( ERROR_SUCCESS == dwStatus ) { dwStatus = RegOpenKeyEx ( m_hKeyMachine, TEXT("System\\CurrentControlSet\\Services\\SysmonLog"), 0, KEY_READ | KEY_WRITE, &hKeyPerfLog); if (dwStatus != ERROR_SUCCESS) { // unable to access the key for write access, so try read only
dwStatus = RegOpenKeyEx ( m_hKeyMachine, TEXT("System\\CurrentControlSet\\Services\\SysmonLog"), 0, KEY_READ, &hKeyPerfLog); if ( ERROR_SUCCESS == dwStatus ) { bReadOnlyPerfLogKey = TRUE; } } }
EnterCriticalSection ( &g_critsectInstallDefaultQueries );
// In Windows 2000, the Log Queries key is created by the snapin.
// After Windows 2000, the Log Queries key is created by system setup,
// along with a "Default Installed" registry flag.
if ( ERROR_SUCCESS == dwStatus && NULL == m_hKeyLogService ) {
if ( !bReadOnlyPerfLogKey ) { // add registry subkey for Log Queries
dwStatus = RegCreateKeyEx ( hKeyPerfLog, TEXT("Log Queries"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &m_hKeyLogService, &dwDisposition); } else { // ReadOnly SysmonLog key. Still possible to succeed if Log Queries
// exists with read/write access.
dwStatus = RegOpenKeyEx ( m_hKeyMachine, TEXT("System\\CurrentControlSet\\Services\\SysmonLog\\Log Queries"), 0, KEY_READ | KEY_WRITE, &m_hKeyLogService); if (dwStatus == ERROR_SUCCESS) { bReadOnlyLogQueriesKey = FALSE; } else { // unable to access the key for write access, so try read only
dwStatus = RegOpenKeyExW ( m_hKeyMachine, (LPCWSTR)L"SYSTEM\\CurrentControlSet\\Services\\SysmonLog\\Log Queries", 0, KEY_READ, &m_hKeyLogService);
if ( ERROR_SUCCESS == dwStatus ) { bReadOnlyLogQueriesKey = TRUE; } } } } else if ( m_bReadOnly ) { bReadOnlyLogQueriesKey = TRUE; }
if ( ERROR_SUCCESS == dwStatus ) { // Log queries key now exists.
strValueName = CGlobalString::m_cszDefaultsInstalled; dwType = REG_DWORD; dwRegValue = 0; dwBufferSize = sizeof(DWORD);
dwStatus = RegQueryValueExW ( m_hKeyLogService, strValueName, NULL, &dwType, (LPBYTE)&dwRegValue, &dwBufferSize );
if ( ERROR_SUCCESS != dwStatus || 0 == dwRegValue ) { if ( !bReadOnlyLogQueriesKey ) { // Create default counter log query.
// Todo: Message on error.
dwStatus = CreateDefaultLogQueries(); if ( ERROR_SUCCESS == dwStatus ) { dwRegValue = SLQ_DEFAULT_SYS_QUERY; dwStatus = RegSetValueEx ( m_hKeyLogService, strValueName, 0L, REG_DWORD, (CONST BYTE *)&dwRegValue, dwBufferSize); } } else { dwStatus = SMCFG_NO_INSTALL_ACCESS; } } } if ( ERROR_SUCCESS == dwStatus ) { RegFlushKey ( m_hKeyLogService ); // Ignore status.
} LeaveCriticalSection ( &g_critsectInstallDefaultQueries );
if (NULL != hKeyPerfLog ) { RegCloseKey (hKeyPerfLog); }
if ( ERROR_ACCESS_DENIED == dwStatus ) { dwStatus = SMCFG_NO_INSTALL_ACCESS; } return dwStatus; }
DWORD CSmLogService::Synchronize( void ) { // If the service is running, tell it to synchronize itself,
// Check the state afterwards to see if it got the message.
// If stop pending or stopped, wait until the service is
// stopped and then attempt to start it. The service
// synchronizes itself from the registry when it is started.
// Return 0 for success, other for failure.
SC_HANDLE hSC = NULL; SC_HANDLE hService = NULL; SERVICE_STATUS ssData; DWORD dwCurrentState; DWORD dwTimeout = 50; LONG dwStatus = ERROR_SUCCESS; BOOL bServiceStarted = FALSE; dwCurrentState = GetState();
if ( 0 == dwCurrentState ) { dwStatus = 1; } else { // open SC database
hSC = OpenSCManager ( GetMachineName(), NULL, GENERIC_READ); if ( NULL != hSC ) { // open service
hService = OpenService ( hSC, TEXT("SysmonLog"), SERVICE_USER_DEFINED_CONTROL | SERVICE_START );
if ( NULL != hService ) { if ( ( SERVICE_STOPPED != dwCurrentState ) && ( SERVICE_STOP_PENDING != dwCurrentState ) ) { // Wait 100 milliseconds before synchronizing service,
// to ensure that registry values are written.
Sleep ( 100 );
ControlService ( hService, SERVICE_CONTROL_SYNCHRONIZE, &ssData); dwCurrentState = ssData.dwCurrentState; }
// Make sure that the ControlService call reached the service
// while it was in run state.
if ( ( SERVICE_STOPPED == dwCurrentState ) || ( SERVICE_STOP_PENDING == dwCurrentState ) ) { if ( SERVICE_STOP_PENDING == dwCurrentState ) { // wait for the service to stop before starting it.
while (--dwTimeout) { dwCurrentState = GetState(); if ( SERVICE_STOP_PENDING == dwCurrentState ) { Sleep(200); } else { break; } } }
dwTimeout = 50; if ( SERVICE_STOPPED == dwCurrentState ) { bServiceStarted = StartService (hService, 0, NULL); if ( !bServiceStarted ) { dwStatus = GetLastError(); if ( ERROR_SERVICE_ALREADY_RUNNING == dwStatus ) { // Okay if started during the time window since
// the last GetState() call.
dwStatus = ERROR_SUCCESS; bServiceStarted = TRUE; } // else error
}
if ( bServiceStarted ) { // wait for the service to start or stop
// before returning
while (--dwTimeout) { dwCurrentState = GetState(); if ( SERVICE_START_PENDING == dwCurrentState ) { Sleep(200); } else { break; } } } } } CloseServiceHandle (hService); } else { dwStatus = GetLastError(); } CloseServiceHandle (hSC);
} else { dwStatus = GetLastError(); } } // Update the Auto Start service config.
if ( ERROR_SUCCESS == dwStatus ) { // Ignore errors
UpdateConfig(); } return dwStatus; }
void CSmLogService::SetBaseName( const CString& rstrName ) { // This method is only called within the service constructor,
// so throw any errors
m_strBaseName = rstrName; return; }
const CString& CSmLogService::GetDefaultLogFileFolder() { if ( m_strDefaultLogFileFolder.IsEmpty() ) { LoadDefaultLogFileFolder(); } return m_strDefaultLogFileFolder; }
INT CSmLogService::GetQueryCount() { INT iQueryCount = -1; // The query count is only valid if the service is open.
if ( IsOpen() ) { iQueryCount = (int) m_QueryList.GetCount(); } else { ASSERT ( FALSE ); } return iQueryCount; }
DWORD CSmLogService::FindDuplicateQuery ( const CString cstrName, BOOL& rbFound ) { DWORD dwStatus = ERROR_SUCCESS; HRESULT hrLocal = NOERROR; DWORD dwQueryIndex = 0; LONG lEnumStatus = ERROR_SUCCESS; WCHAR szQueryKeyName[MAX_PATH + 1]; DWORD dwQueryKeyNameLen; LPTSTR szCollectionName = NULL; UINT uiCollectionNameLen = 0; FILETIME ftLastWritten; HKEY hKeyQuery = NULL; BOOL bFoundFirst = FALSE; CString strDirectName; CString strLocalName;
ASSERT ( !cstrName.IsEmpty() );
rbFound = FALSE; if ( !cstrName.IsEmpty() ) {
MFC_TRY strLocalName = cstrName; MFC_CATCH_DWSTATUS;
if ( ERROR_SUCCESS == dwStatus ) {
// Translate new query name if necessary
hrLocal = SHLoadIndirectString( strLocalName.GetBufferSetLength ( strLocalName.GetLength() ), strDirectName.GetBufferSetLength ( MAX_PATH ), MAX_PATH, NULL );
strLocalName.ReleaseBuffer(); strDirectName.ReleaseBuffer();
if ( FAILED ( hrLocal ) ) { // Query name is not an indirect string
dwStatus = ERROR_SUCCESS; MFC_TRY strDirectName = strLocalName; MFC_CATCH_DWSTATUS; } } }
if ( ERROR_SUCCESS == dwStatus ) {
// Search all queries for the specified query.
dwQueryKeyNameLen = sizeof ( szQueryKeyName ) / sizeof ( WCHAR ); memset (szQueryKeyName, 0, sizeof (szQueryKeyName));
while ( ERROR_SUCCESS == ( lEnumStatus = RegEnumKeyExW ( m_hKeyLogService, dwQueryIndex, szQueryKeyName, &dwQueryKeyNameLen, NULL, NULL, NULL, &ftLastWritten ) ) ) {
// open the query specified
dwStatus = RegOpenKeyExW ( m_hKeyLogService, szQueryKeyName, 0, KEY_READ, &hKeyQuery);
if ( ERROR_SUCCESS == dwStatus ) {
// Query key is Guid if written by post Win2000 snapin.
// Query key is name if written by Win2000 snapin.
if ( 0 == strDirectName.CompareNoCase ( szQueryKeyName ) ) { if ( TRUE == bFoundFirst ) { rbFound = TRUE; break; } else { bFoundFirst = TRUE; } } else {
dwStatus = SmReadRegistryIndirectStringValue ( hKeyQuery, L"Collection Name", NULL, &szCollectionName, &uiCollectionNameLen );
ASSERT ( MAX_PATH >= uiCollectionNameLen );
if ( ERROR_SUCCESS == dwStatus ) { if ( MAX_PATH >= uiCollectionNameLen ) { if ( NULL != szCollectionName ) { if ( L'\0' == *szCollectionName ) { G_FREE ( szCollectionName ); szCollectionName = NULL; } }
if ( NULL == szCollectionName ) { MFC_TRY szCollectionName = (LPWSTR)G_ALLOC ( (lstrlen(szQueryKeyName)+1)*sizeof(WCHAR)); MFC_CATCH_DWSTATUS; if ( ERROR_SUCCESS == dwStatus ) { lstrcpyn ( szCollectionName, szQueryKeyName, lstrlen(szQueryKeyName)+1 ); } }
if ( NULL != szCollectionName ) {
// Compare found name to input name.
if ( 0 == strDirectName.CompareNoCase ( szCollectionName ) ) { if ( TRUE == bFoundFirst ) { rbFound = TRUE; break; } else { bFoundFirst = TRUE; } } } // Todo: else report message?
} } // Todo: else report message?
} // Todo: else report message?
}
// set up for the next item in the list
dwQueryKeyNameLen = sizeof (szQueryKeyName) / sizeof (szQueryKeyName[0]); memset (szQueryKeyName, 0, sizeof (szQueryKeyName)); if ( NULL != hKeyQuery ) { RegCloseKey( hKeyQuery ); hKeyQuery = NULL; }
if ( NULL != szCollectionName ) { G_FREE ( szCollectionName ); szCollectionName = NULL; uiCollectionNameLen = 0; } dwQueryIndex++; } } else { dwStatus = ERROR_INVALID_PARAMETER; }
if ( NULL != szCollectionName ) { G_FREE ( szCollectionName ); szCollectionName = NULL; uiCollectionNameLen = 0; }
if ( NULL != hKeyQuery ) { RegCloseKey( hKeyQuery ); hKeyQuery = NULL; }
return dwStatus; }
BOOL CSmLogService::IsWindows2000Server ( void ) { if ( 5 == m_OSVersion.dwMajorVersion && 2195 == m_OSVersion.dwBuild ) { return TRUE; }
return FALSE; } BOOL CSmLogService::CanAccessWbemRemote() { HRESULT hr; IWbemLocator *pLocator = NULL; IWbemServices* pWbemServices = NULL; LPCWSTR szRoot[2] = { L"root\\perfmon", L"root\\wmi" }; LPCWSTR szMask = L"\\\\%s\\%s"; BSTR bszClass = SysAllocString(L"SysmonLog"); BSTR bszNamespace = NULL; LPWSTR buffer = NULL; DWORD dwBufLen;
hr = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator );
if (!SUCCEEDED(hr)) { goto Cleanup; }
if ( !GetMachineName().IsEmpty()) {
dwBufLen = max(wcslen(szRoot[0]), wcslen(szRoot[1])) + GetMachineName().GetLength() + wcslen( szMask );
buffer = new WCHAR[dwBufLen];
if ( buffer == NULL ){ hr = ERROR_OUTOFMEMORY; goto Cleanup; } }
for (int i = 0; i < 2; i++) { if (bszNamespace) { SysFreeString(bszNamespace); bszNamespace = NULL; } if (buffer) { swprintf( buffer, szMask, GetMachineName(), szRoot[i] ); bszNamespace = SysAllocString( buffer ); } else { bszNamespace = SysAllocString(szRoot[i]); }
hr = pLocator->ConnectServer( bszNamespace, NULL, NULL, NULL, 0, NULL, NULL, &pWbemServices); if (SUCCEEDED(hr)) { break; } }
Cleanup: if (buffer) { delete [] buffer; }
if (bszNamespace) { SysFreeString(bszNamespace); }
if (pLocator) { pLocator->Release(); }
if (pWbemServices) { pWbemServices->Release(); }
m_hWbemAccessStatus = hr; if (SUCCEEDED(hr)) { return TRUE; } else { return FALSE; } }
|