/*++ Copyright (C) 1999-2001 Microsoft Corporation Module Name: ADAPREG.CPP Abstract: History: --*/ #include "precomp.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "adapreg.h" #include "perflibschema.h" #include "WMIBroker.h" #include "adaputil.h" #include "adapperf.h" #include "ntreg.h" #include "winuser.h" // globals DWORD CAdapPerfLib::s_MaxSizeCollect = 64*1024*1024; // Performance library processing list // =================================== CPerfLibList g_PerfLibList; HANDLE g_hAbort = NULL; ///////////////////////////////////////////////////////////////////////////// // // returns the PID (the first found if many are there) if success // returns ZERO if fails // ///////////////////////////////////////////////////////////////////////////// #define MAX_ITERATION 8 #define MAX_MODULE (1024) DWORD GetExecPid() { DWORD ThisProc = 0; LONG lRet = 0; HKEY hKey; lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\WBEM\\CIMOM", NULL, KEY_READ, &hKey); if (ERROR_SUCCESS == lRet) { DWORD dwType; DWORD dwSize = sizeof(DWORD); RegQueryValueEx(hKey, L"ProcessID", NULL, &dwType, (BYTE*)&ThisProc, &dwSize); RegCloseKey(hKey); } return ThisProc; } void DoResyncPerf( BOOL bDelta, BOOL bThrottle ) { DEBUGTRACE((LOG_WINMGMT,"ADAP Resync has started\n")); g_hAbort = CreateEventW( NULL, TRUE, FALSE, L"ADAP_ABORT"); CCloseMe cmAbort( g_hAbort ); if ( NULL != g_hAbort ) { HRESULT hr = WBEM_S_NO_ERROR; CAdapRegPerf regPerf(!bDelta); // Process each perflib that is registered on the system // ===================================================== hr = regPerf.Initialize( bDelta, bThrottle ); if ( SUCCEEDED( hr ) ) { hr = regPerf.Dredge( bDelta, bThrottle ); } if ( FAILED( hr ) ) { CAdapUtility::NTLogEvent( EVENTLOG_WARNING_TYPE, WBEM_MC_ADAP_PROCESSING_FAILURE, CHex( hr ) ); } } DEBUGTRACE((LOG_WINMGMT,"ADAP Resync has completed\n")); return; } void DoClearADAP() { DEBUGTRACE((LOG_WINMGMT,"ADAP registry reset has started\n")); CAdapRegPerf regPerf(FALSE); regPerf.Clean(); DEBUGTRACE((LOG_WINMGMT,"ADAP registry reset has completed\n")); return; } HRESULT DoReverseAdapterMaintenance( BOOL bThrottle ); /* { ERRORTRACE((LOG_WMIADAP,"DoReverseAdapterDredge called")); return WBEM_NO_ERROR; }; */ /* ->Revision: 0x1 ->Sbz1 : 0x0 ->Control : 0x8004 SE_DACL_PRESENT SE_SELF_RELATIVE ->Owner : S-1-5-32-544 ->Group : S-1-5-18 ->Dacl : ->Dacl : ->AclRevision: 0x2 ->Dacl : ->Sbz1 : 0x0 ->Dacl : ->AclSize : 0x44 ->Dacl : ->AceCount : 0x2 ->Dacl : ->Sbz2 : 0x0 ->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[0]: ->AceFlags: 0x0 ->Dacl : ->Ace[0]: ->AceSize: 0x14 ->Dacl : ->Ace[0]: ->Mask : 0x001f0003 ->Dacl : ->Ace[0]: ->SID: S-1-5-18 ->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[1]: ->AceFlags: 0x0 ->Dacl : ->Ace[1]: ->AceSize: 0x18 ->Dacl : ->Ace[1]: ->Mask : 0x001f0003 ->Dacl : ->Ace[1]: ->SID: S-1-5-32-544 */ DWORD g_PreCompSD[] = { 0x80040001 , 0x00000058 , 0x00000068 , 0x00000000, 0x00000014 , 0x00440002 , 0x00000002 , 0x00140000, 0x001f0003 , 0x00000101 , 0x05000000 , 0x00000012, 0x00180000 , 0x001f0003 , 0x00000201 , 0x05000000, 0x00000020 , 0x00000220 , 0x00b70000 , 0x00000000, 0x01190000 , 0x00010002 , 0x00000201 , 0x05000000, 0x00000020 , 0x00000220 , 0x00000101 , 0x05000000, 0x00000012 , 0x00000069 , 0x00000000 , 0x00000000 }; // // Build a SD with owner == ProcessSid // group == ProcessSid // DACL // ACE[0] MUTEX_ALL_ACCESS System // ACE[1] MUTEX_ALL_ACCESS Administators /////////////////////////////////////////////////////////////////// VOID * CreateSD(/* in */ DWORD AccessMask, /* out */ DWORD &SizeSd ) { SizeSd = 0; HANDLE hToken; if (FALSE == OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hToken)) return NULL; OnDelete CloseToken(hToken); DWORD dwSize = sizeof(TOKEN_USER)+sizeof(SID)+(SID_MAX_SUB_AUTHORITIES*sizeof(DWORD)); TOKEN_USER * pToken_User = (TOKEN_USER *)LocalAlloc(LPTR,dwSize); if (NULL == pToken_User) return NULL; OnDelete FreeMe1(pToken_User); if (FALSE == GetTokenInformation(hToken,TokenUser,pToken_User,dwSize,&dwSize)) return NULL; SID_IDENTIFIER_AUTHORITY ntifs = SECURITY_NT_AUTHORITY; PSID SystemSid = NULL; if (FALSE == AllocateAndInitializeSid( &ntifs , 1, SECURITY_LOCAL_SYSTEM_RID,0,0,0,0,0,0,0, &SystemSid)) return NULL; OnDelete FreeSid1(SystemSid); PSID AdministratorsSid = NULL; if (FALSE == AllocateAndInitializeSid(&ntifs, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,0,0,0,0,0,0, &AdministratorsSid)) return NULL; OnDelete FreeSid2(AdministratorsSid); PSID pSIDUser = pToken_User->User.Sid; dwSize = GetLengthSid(pSIDUser); DWORD dwSids = 2; // System and Administrators DWORD ACLLength = (ULONG) sizeof(ACL) + (dwSids * ((ULONG) sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG))) + GetLengthSid(SystemSid) + GetLengthSid(AdministratorsSid); DWORD dwSizeSD = sizeof(SECURITY_DESCRIPTOR_RELATIVE) + dwSize + dwSize + ACLLength; SECURITY_DESCRIPTOR_RELATIVE * pLocalSD = (SECURITY_DESCRIPTOR_RELATIVE *)LocalAlloc(LPTR,dwSizeSD); if (NULL == pLocalSD) return NULL; OnDeleteIf FreeMeSD(pLocalSD); memset(pLocalSD,0,sizeof(SECURITY_DESCRIPTOR_RELATIVE)); pLocalSD->Revision = SECURITY_DESCRIPTOR_REVISION; pLocalSD->Control = SE_DACL_PRESENT|SE_SELF_RELATIVE; //SetSecurityDescriptorOwner(pLocalSD,pSIDUser,FALSE); memcpy((BYTE*)pLocalSD+sizeof(SECURITY_DESCRIPTOR_RELATIVE),pSIDUser,dwSize); pLocalSD->Owner = (DWORD)sizeof(SECURITY_DESCRIPTOR_RELATIVE); //SetSecurityDescriptorGroup(pLocalSD,pSIDUser,FALSE); memcpy((BYTE*)pLocalSD+sizeof(SECURITY_DESCRIPTOR_RELATIVE)+dwSize,pSIDUser,dwSize); pLocalSD->Group = (DWORD)(sizeof(SECURITY_DESCRIPTOR_RELATIVE)+dwSize); PACL pDacl = (PACL)LocalAlloc(LPTR,ACLLength); if (NULL == pDacl) return NULL; OnDelete FreeMe3(pDacl); if (FALSE == InitializeAcl( pDacl,ACLLength,ACL_REVISION)) return NULL; if (FALSE == AddAccessAllowedAceEx (pDacl,ACL_REVISION,0,AccessMask,SystemSid)) return NULL; if (FALSE == AddAccessAllowedAceEx (pDacl,ACL_REVISION,0,AccessMask,AdministratorsSid)) return NULL; //bRet = SetSecurityDescriptorDacl(pLocalSD,TRUE,pDacl,FALSE); memcpy((BYTE*)pLocalSD+sizeof(SECURITY_DESCRIPTOR_RELATIVE)+dwSize+dwSize,pDacl,ACLLength); pLocalSD->Dacl = (DWORD)(sizeof(SECURITY_DESCRIPTOR_RELATIVE)+dwSize+dwSize); if (false == RtlValidRelativeSecurityDescriptor(pLocalSD, dwSizeSD, OWNER_SECURITY_INFORMATION| GROUP_SECURITY_INFORMATION| DACL_SECURITY_INFORMATION)) return NULL; FreeMeSD.dismiss(); SizeSd = dwSizeSD; return pLocalSD; }; /////////////////////////////////////////////////////////////////////////////// // // Entry Point // =========== // /////////////////////////////////////////////////////////////////////////////// int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR szCmdLine, // command line int nCmdShow // show state ) { try { if (CStaticCritSec::anyFailure()) return 0; // Ensure that we are NT5 or better // ================================ OSVERSIONINFO OSVer; OSVer.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); if ( GetVersionEx( &OSVer ) ) { if ( ! ( ( VER_PLATFORM_WIN32_NT == OSVer.dwPlatformId ) && ( 5 <= OSVer.dwMajorVersion ) ) ) return 0; } else { return 0; } // To avoid messy dialog boxes... // ============================== SetErrorMode( SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX ); // Initialize COM. RETURN_ON_ERR(CoInitializeEx(NULL,COINIT_MULTITHREADED)); OnDelete0 CoUninit; RETURN_ON_ERR(CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE|EOAC_SECURE_REFS , NULL )); // get some value from registry CNTRegistry reg; if ( CNTRegistry::no_error == reg.Open( HKEY_LOCAL_MACHINE, WBEM_REG_WINMGMT) ) { reg.GetDWORD( ADAP_KEY_MAX_COLLECT, &CAdapPerfLib::s_MaxSizeCollect); } // Get the Winmgmt service PID // =========================== DWORD dwPID = GetExecPid(); // The semaphore is used so that no more that two copies are running at any time. // ============================================================================== WCHAR wszObjName[256]; HANDLE hSemaphore; DWORD SizeSd; void * pSecDesSem = CreateSD(SEMAPHORE_ALL_ACCESS,SizeSd); OnDelete FreeMeSem(pSecDesSem); SECURITY_ATTRIBUTES sa; sa.nLength = (pSecDesSem) ? SizeSd : sizeof(g_PreCompSD); sa.lpSecurityDescriptor = (pSecDesSem) ? pSecDesSem : g_PreCompSD; sa.bInheritHandle = FALSE; StringCchPrintfW(wszObjName, 256, L"Global\\WMI_SysEvent_Semaphore_%d", dwPID); hSemaphore = CreateSemaphoreW(&sa, 2, 2, wszObjName); if(hSemaphore == NULL) { DEBUGTRACE((LOG_WMIADAP,"WMI_SysEvent_Semaphore semaphore creation failed %d\n",GetLastError())); return 0; } CCloseMe cm1(hSemaphore); DWORD dwRet = WaitForSingleObject(hSemaphore, 0); if(dwRet != WAIT_OBJECT_0) return 0; // The mutex makes sure that multiple copies are sequential. // ========================================================= void * pSecDesMut = CreateSD(MUTEX_ALL_ACCESS,SizeSd); OnDelete FreeMeMut(pSecDesMut); sa.nLength = (pSecDesMut) ? SizeSd : sizeof(g_PreCompSD); sa.lpSecurityDescriptor = (pSecDesMut) ? pSecDesSem : g_PreCompSD; sa.bInheritHandle = FALSE; HANDLE hMutex; hMutex = CreateMutexW( &sa, FALSE, L"Global\\ADAP_WMI_ENTRY" ); if(hMutex == NULL) { DEBUGTRACE((LOG_WMIADAP,"ADAP_WMI_ENTRY mutex creation failed %d\n",GetLastError())); return 0; } CCloseMe cm2(hMutex); switch ( WaitForSingleObject( hMutex, 400000) ) { case WAIT_ABANDONED: case WAIT_OBJECT_0: { BOOL bThrottle = FALSE; BOOL bFull = FALSE; BOOL bDelta = FALSE; BOOL bReverse = FALSE; BOOL bClear = FALSE; if (szCmdLine) { while (*szCmdLine) { while(*szCmdLine && isspace((UCHAR)*szCmdLine)){ szCmdLine++; }; if (*szCmdLine == '-' || *szCmdLine == '/') { szCmdLine++; if (toupper((UCHAR)*szCmdLine) == 'T'){ bThrottle = TRUE; } else if (toupper((UCHAR)*szCmdLine) == 'R') { bReverse = TRUE; } else if (toupper((UCHAR)*szCmdLine) == 'F') { bFull = TRUE; } else if (toupper((UCHAR)*szCmdLine) == 'D') { bDelta = TRUE; } else if (toupper((UCHAR)*szCmdLine) == 'C') { bClear = TRUE; } } // move to the next white space while(*szCmdLine && !isspace(*szCmdLine)){ szCmdLine++; } } } if (bClear) // ClearADAP and/or ReverseAdap { DoClearADAP(); if (bReverse) DoReverseAdapterMaintenance( bThrottle ); } else { if (!bFull && !bDelta && !bReverse) { // no options, use Delta NO-THROTTLE DoResyncPerf(TRUE,FALSE); } else { if (bFull) { DoResyncPerf(FALSE,bThrottle); } if (bDelta && !bFull) { DoResyncPerf(TRUE,bThrottle); } if (bReverse) DoReverseAdapterMaintenance( bThrottle ); } } ReleaseMutex( hMutex ); }break; } long l; ReleaseSemaphore(hSemaphore, 1, &l); } catch(...) { // We have been betrayed... try to write something to the error log // ======================================================================= CriticalFailADAPTrace( "An unhandled exception has been thrown in the main thread." ); } return 0; } //////////////////////////////////////////////////////////////////////////////// // // CPerfLibList // //////////////////////////////////////////////////////////////////////////////// HRESULT CPerfLibList::AddPerfLib( WCHAR* wszPerfLib ) { HRESULT hr = WBEM_S_NO_ERROR; CInCritSec ics( &m_csPerfLibList ); try { // Compute the size of the new buffer // ================================== DWORD dwListSize = 0; if ( NULL != m_wszPerfLibList ) { dwListSize += wcslen( m_wszPerfLibList ); dwListSize += wcslen( ADAP_EVENT_MESSAGE_DELIM ); } if ( NULL != wszPerfLib ) { dwListSize += wcslen( wszPerfLib ); } // Create the new buffer, and initialize the content // ================================================= size_t cchSizeTmp = dwListSize + 1; WCHAR* wszNew = new WCHAR[cchSizeTmp]; if (NULL == wszNew) return WBEM_E_OUT_OF_MEMORY; // Copy the old buffer if required // =============================== if ( NULL != m_wszPerfLibList ) { StringCchPrintfW( wszNew, cchSizeTmp, L"%s%s%s", m_wszPerfLibList, ADAP_EVENT_MESSAGE_DELIM, wszPerfLib ); delete [] m_wszPerfLibList; } else { StringCchCopyW(wszNew, cchSizeTmp, wszPerfLib); } // And assign it to the static member // ================================== m_wszPerfLibList = wszNew; } catch(...) { hr = WBEM_E_FAILED; } return hr; } HRESULT CPerfLibList::HandleFailure() { HRESULT hr = WBEM_S_NO_ERROR; CInCritSec ics( &m_csPerfLibList ); try { char szMessage[ADAP_EVENT_MESSAGE_LENGTH]; DWORD dwMessageLen = strlen( ADAP_EVENT_MESSAGE_PREFIX ); if ( NULL != m_wszPerfLibList ) { dwMessageLen += wcslen( m_wszPerfLibList ); } StringCchPrintfA(szMessage,ADAP_EVENT_MESSAGE_LENGTH, "%s%S\n", ADAP_EVENT_MESSAGE_PREFIX, (NULL != m_wszPerfLibList) ? m_wszPerfLibList : L"" ); CriticalFailADAPTrace( szMessage ); } catch(...) { hr = WBEM_E_FAILED; } return hr; } //////////////////////////////////////////////////////////////////////////////// // // Static Members // //////////////////////////////////////////////////////////////////////////////// LONG CAdapRegPerf::AdapUnhandledExceptionFilter( LPEXCEPTION_POINTERS lpexpExceptionInfo ) { g_PerfLibList.HandleFailure(); return EXCEPTION_CONTINUE_SEARCH; } //////////////////////////////////////////////////////////////////////////////// // // CAdapRegPerf // //////////////////////////////////////////////////////////////////////////////// CAdapRegPerf::CAdapRegPerf(BOOL bFull) : m_pLocaleCache( NULL ), m_fQuit( FALSE ), m_dwPID( 0 ), m_pADAPStatus( NULL ), m_pRootDefault( NULL ), m_hRegChangeEvent( NULL ), m_hPerflibKey( NULL ), m_pKnownSvcs(NULL), m_bFull(bFull) { for ( DWORD dwType = 0; dwType < WMI_ADAP_NUM_TYPES; dwType++ ) m_apMasterClassList[dwType] = NULL; } CAdapRegPerf::~CAdapRegPerf() { // Status: COMPLETE // ================ SetADAPStatus( eADAPStatusFinished); // // Add TimeStamp to registry if FULL // if (m_bFull) { FILETIME FileTime; GetSystemTimeAsFileTime(&FileTime); LONG lRet; HKEY hKey; lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\WBEM\\CIMOM", NULL, KEY_WRITE, &hKey); if (ERROR_SUCCESS == lRet) { RegSetValueEx(hKey, ADAP_TIMESTAMP_FULL, NULL, REG_BINARY, (BYTE*)&FileTime, sizeof(FILETIME)); RegCloseKey(hKey); } } if (m_pKnownSvcs) { m_pKnownSvcs->Save(); m_pKnownSvcs->Release(); } // Cleanup // ======= for ( DWORD dwType = 0; dwType < WMI_ADAP_NUM_TYPES; dwType++ ) { if ( NULL != m_apMasterClassList[dwType] ) { m_apMasterClassList[dwType]->Release(); } } if ( NULL != m_pLocaleCache ) { m_pLocaleCache->Release(); } if ( NULL != m_pRootDefault ) { m_pRootDefault->Release(); } if ( NULL != m_pADAPStatus ) { m_pADAPStatus->Release(); } if ( NULL != m_hPerflibKey ) { RegCloseKey( m_hPerflibKey ); } if ( NULL != m_hRegChangeEvent ) { CloseHandle( m_hRegChangeEvent ); } SetEvent( m_hTerminationEvent ); } HRESULT CAdapRegPerf::Initialize(BOOL bDelta, BOOL bThrottle) /////////////////////////////////////////////////////////////////////////////// // // Initialize is responsible for setting up the dredging environment. The // unhandled exception filter is set to handle any exceptions throw and not // handled by perforance libraries. The termination event is a signal used // to identify when the process is being abnormally terminated. The // GoGershwin thread is suitably named since it is something to watch over // over the main process. The locale cache is a cache of all locales // available in the performance domain (enumeration of the names' database // subkeys). The master class lists for both the cooked and the raw classes // represent the state of the performance objects in WMI. // // Parameters: // none // /////////////////////////////////////////////////////////////////////////////// { HRESULT hr = WBEM_NO_ERROR; // Initialize the root\default pointer. This will be used to track our status // ========================================================================== GetADAPStatusObject(); // Set the filter for handling unhandled exceptions thrown in threads generated by the perflibs // ============================================================================================ SetUnhandledExceptionFilter( CAdapRegPerf::AdapUnhandledExceptionFilter ); // ADAP termination event // ====================== m_hTerminationEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if ( NULL == m_hTerminationEvent ) { hr = WBEM_E_FAILED; } // Open the registry key to be monitored // ===================================== if ( SUCCEEDED( hr ) ) { if ( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Perflib"), 0, KEY_NOTIFY, &m_hPerflibKey ) ) { hr = WBEM_E_FAILED; } } // Create the registry change event // ================================ if ( SUCCEEDED( hr ) ) { m_hRegChangeEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if ( NULL == m_hRegChangeEvent ) { hr = WBEM_E_FAILED; } } // Create the names' database change notification // ============================================== // Note that we are only looking for subkeys being added or deleted. We do // not want to monitor the registry values since the status and signature // values may be changing throughout the course of the dredge, and we do // not want to cause a re-cache unless a performance library is added // (i.e. a performance subkey is added if ( SUCCEEDED( hr ) ) { if ( ERROR_SUCCESS != RegNotifyChangeKeyValue( m_hPerflibKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, m_hRegChangeEvent, TRUE ) ) { hr = WBEM_E_FAILED; } } // Get the WinMgmt Service PID // =========================== if ( SUCCEEDED( hr ) ) { m_dwPID = GetExecPid(); } // Create the "Someone to watch over me" thread // ============================================ if ( SUCCEEDED( hr ) ) { UINT nThreadID = 0; m_hSyncThread = ( HANDLE ) _beginthreadex( NULL, 0, CAdapRegPerf::GoGershwin, (void*) this, 0, &nThreadID ); DEBUGTRACE ( ( LOG_WMIADAP, "The Monitor thread ID is 0x%x\n", nThreadID ) ); if ( (HANDLE)-1 == m_hSyncThread ) { hr = WBEM_E_FAILED; } } // Set up the locale cache // ======================= if ( SUCCEEDED( hr ) ) { m_pLocaleCache = new CLocaleCache( ); if ( NULL == m_pLocaleCache ) { hr = WBEM_E_OUT_OF_MEMORY; } else { hr = m_pLocaleCache->Initialize(); } } // // m_pKnownSvcs = new CKnownSvcs(KNOWN_SERVICES); if (m_pKnownSvcs) m_pKnownSvcs->Load(); // Set up the master class lists for the raw classes // ================================================= if ( SUCCEEDED( hr ) ) { m_apMasterClassList[WMI_ADAP_RAW_CLASS] = new CMasterClassList( m_pLocaleCache, m_pKnownSvcs ); if ( NULL != m_apMasterClassList[WMI_ADAP_RAW_CLASS] ) { hr = m_apMasterClassList[WMI_ADAP_RAW_CLASS]->BuildList( ADAP_PERF_RAW_BASE_CLASS, bDelta, bThrottle ); } else { hr = WBEM_E_OUT_OF_MEMORY; } } // Set up the master class lists for the cooked classes // ==================================================== if ( SUCCEEDED( hr ) ) { m_apMasterClassList[WMI_ADAP_COOKED_CLASS] = new CMasterClassList( m_pLocaleCache, m_pKnownSvcs ); if ( NULL != m_apMasterClassList[WMI_ADAP_COOKED_CLASS] ) { m_apMasterClassList[WMI_ADAP_COOKED_CLASS]->BuildList( ADAP_PERF_COOKED_BASE_CLASS, bDelta, bThrottle ); } else { hr = WBEM_E_OUT_OF_MEMORY; } } #ifdef _DUMP_LIST m_apMasterClassList[WMI_ADAP_RAW_CLASS]->Dump(); m_apMasterClassList[WMI_ADAP_COOKED_CLASS]->Dump(); #endif return hr; } HRESULT CAdapRegPerf::Dredge( BOOL bDelta, BOOL bThrottle ) /////////////////////////////////////////////////////////////////////////////// // // This is the entry point method which dredges the registry for performance // counters and registers the classes in WMI. This method enumerates all of // the service keys looking for 'Performance' subkeys which indicate // performance libraries. If a library is discovered, then it is sent to // the ProcessLibrary method for, you guessed it, processing. // // Parameters: // none // /////////////////////////////////////////////////////////////////////////////// { HRESULT hr = WBEM_S_NO_ERROR; WString wstrServiceKey, wstrPerformanceKey; if ( SUCCEEDED( hr ) ) { // Status: PROCESSING // ================== SetADAPStatus( eADAPStatusProcessLibs); // Open the services key // ===================== long lError = Open( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services" ); if ( CNTRegistry::no_error == lError ) { // Iterate through the services list // ================================= DWORD dwIndex = 0; DWORD dwBuffSize = 0; wmilib::auto_buffer pwcsServiceName; while ( ( CNTRegistry::no_error == lError ) && ( !m_fQuit ) ) { // Reset the processing status // =========================== hr = WBEM_NO_ERROR; if ( WAIT_OBJECT_0 == WaitForSingleObject( m_hRegChangeEvent, 0 ) ) { m_pLocaleCache->Reset(); dwIndex = 0; // Reset the event and reset the change notification ResetEvent( m_hRegChangeEvent ); RegNotifyChangeKeyValue( m_hPerflibKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, m_hRegChangeEvent, TRUE ); } // For each service name, we will check for a performance // key and if it exists, we will process the library // ====================================================== lError = Enum( dwIndex, pwcsServiceName , dwBuffSize ); if (bThrottle) { HRESULT hrThr = Throttle(THROTTLE_USER|THROTTLE_IO, ADAP_IDLE_USER, ADAP_IDLE_IO, ADAP_LOOP_SLEEP, ADAP_MAX_WAIT); if (THROTTLE_FORCE_EXIT == hrThr) { //OutputDebugStringA("(ADAP) Unthrottle command received\n"); bThrottle = FALSE; UNICODE_STRING BaseUnicodeCommandLine = NtCurrentPeb()->ProcessParameters->CommandLine; WCHAR * pT = wcschr(BaseUnicodeCommandLine.Buffer,L't'); if (0 == pT) pT = wcschr(BaseUnicodeCommandLine.Buffer,L'T'); if (pT) { *pT = L' '; pT--; *pT = L' '; } } } if ( CNTRegistry::no_error == lError ) { try { // Create the perfomance key path // ============================== wstrServiceKey = L"SYSTEM\\CurrentControlSet\\Services\\"; wstrServiceKey += pwcsServiceName.get(); wstrPerformanceKey = wstrServiceKey; wstrPerformanceKey += L"\\Performance"; } catch( ... ) { hr = WBEM_E_OUT_OF_MEMORY; } if ( SUCCEEDED( hr ) ) { CNTRegistry reg; // Atempt to open the performance registry key for the service // =========================================================== long lPerfError = reg.Open( HKEY_LOCAL_MACHINE, wstrPerformanceKey ); if ( CNTRegistry::no_error == lPerfError ) { // If we can open it, then we have found a perflib! Process it // unless it is the reverse provider perflib // ============================================================= if ( 0 != wbem_wcsicmp( pwcsServiceName.get(), WMI_ADAP_REVERSE_PERFLIB ) ) { hr = ProcessLibrary( pwcsServiceName.get(), bDelta ); } } else if ( CNTRegistry::access_denied == lPerfError ) { ServiceRec * pSvcRec = NULL; if (0 == m_pKnownSvcs->Get(pwcsServiceName.get(),&pSvcRec)) { if (!pSvcRec->IsELCalled()) { CAdapUtility::NTLogEvent( EVENTLOG_WARNING_TYPE, WBEM_MC_ADAP_PERFLIB_REG_VALUE_FAILURE, (LPCWSTR)wstrPerformanceKey, L"Access Denied" ); pSvcRec->SetELCalled(); } } } else { // Otherwise, it is not a perflib service // ====================================== } } } else if ( CNTRegistry::no_more_items != lError ) { if ( CNTRegistry::out_of_memory == lError ) { hr = WBEM_E_OUT_OF_MEMORY; } else { hr = WBEM_E_FAILED; } } dwIndex++; } } else if ( CNTRegistry::access_denied == lError ) { CAdapUtility::NTLogEvent( EVENTLOG_WARNING_TYPE, WBEM_MC_ADAP_PERFLIB_REG_VALUE_FAILURE, L"SYSTEM\\CurrentControlSet\\Services\\", L"Access Denied" ); hr = WBEM_E_FAILED; } else { hr = WBEM_E_FAILED; } // Now that we have a master class list that contains updated // data from all of the perflibs, commit any changes to WMI // ========================================================== if ( SUCCEEDED ( hr ) && ( !m_fQuit ) ) { // Status: COMMIT // ============== SetADAPStatus( eADAPStatusCommit ); for ( DWORD dwType = 0; dwType < WMI_ADAP_NUM_TYPES; dwType++ ) { m_apMasterClassList[dwType]->Commit(bThrottle); } } } if ( SUCCEEDED( hr ) ) { DEBUGTRACE( ( LOG_WMIADAP, "CAdapRegPerf::Dredge() for %S succeeded.\n", (WCHAR *)wstrServiceKey ) ); } else { ERRORTRACE( ( LOG_WMIADAP, "CAdapRegPerf::Dredge() failed: %X.\n", hr ) ); } return hr; } HRESULT CAdapRegPerf::Clean() //////////////////////////////////////////////////////////////////////////////// // // This method enumerates all of the keys from the // HLM\System\CurrentControlSet\Services and searches for a performance subkey. // If a performance subkey is discovered, then any information that was placed // in the key by ADAP is deleted. // // Parameters: // none // //////////////////////////////////////////////////////////////////////////////// { HRESULT hr = WBEM_S_NO_ERROR; WString wstrServiceKey, // The path to the service key wstrPerformanceKey; // The path to the performance subkey CNTRegistry regOuter; // The registry object for the services enumeration // Open the services key // ===================== long lError = regOuter.Open( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services" ); if ( CNTRegistry::no_error == lError ) { // Iterate through the services list // ================================= DWORD dwIndex = 0; DWORD dwBuffSize = 0; wmilib::auto_buffer pwcsServiceName; while ( CNTRegistry::no_error == lError ) { // Reset the processing status // =========================== hr = WBEM_NO_ERROR; // For each service name, we will check for a performance // key and if it exists, we will process the library // ====================================================== lError = regOuter.Enum( dwIndex, pwcsServiceName , dwBuffSize ); if ( CNTRegistry::no_error == lError ) { try { // Create the perfomance key path // ============================== wstrServiceKey = L"SYSTEM\\CurrentControlSet\\Services\\"; wstrServiceKey += pwcsServiceName.get(); wstrPerformanceKey = wstrServiceKey; wstrPerformanceKey += L"\\Performance"; } catch( ... ) { hr = WBEM_E_OUT_OF_MEMORY; } if ( SUCCEEDED( hr ) ) { CNTRegistry regInner; // The registry object for the performance subkey // Atempt to open the performance registry key for the service // =========================================================== long lPerfError = regInner.Open( HKEY_LOCAL_MACHINE, wstrPerformanceKey ); if ( CNTRegistry::no_error == lPerfError ) { // If we can open it, then we have found a perflib! Clean it! // ============================================================= regInner.DeleteValue( ADAP_PERFLIB_STATUS_KEY ); regInner.DeleteValue( ADAP_PERFLIB_SIGNATURE ); regInner.DeleteValue( ADAP_PERFLIB_SIZE ); regInner.DeleteValue( ADAP_PERFLIB_TIME ); } else if ( CNTRegistry::access_denied == lPerfError ) { CAdapUtility::NTLogEvent( EVENTLOG_WARNING_TYPE, WBEM_MC_ADAP_PERFLIB_REG_VALUE_FAILURE, (LPCWSTR)wstrPerformanceKey, L"Access Denied" ); } else { // Otherwise, it is not a perflib service // ====================================== } } } else if ( CNTRegistry::no_more_items != lError ) { if ( CNTRegistry::out_of_memory == lError ) { hr = WBEM_E_OUT_OF_MEMORY; } else { hr = WBEM_E_FAILED; } } dwIndex++; } } else if ( CNTRegistry::access_denied == lError ) { CAdapUtility::NTLogEvent( EVENTLOG_WARNING_TYPE, WBEM_MC_ADAP_PERFLIB_REG_VALUE_FAILURE, L"SYSTEM\\CurrentControlSet\\Services\\", L"Access Denied" ); hr = WBEM_E_FAILED; } else { hr = WBEM_E_FAILED; } if ( SUCCEEDED( hr ) ) { DEBUGTRACE( ( LOG_WMIADAP, "CAdapRegPerf::Clean() succeeded.\n" ) ); } else { ERRORTRACE( ( LOG_WMIADAP, "CAdapRegPerf::Clean() failed: %X.\n", hr ) ); } return hr; } HRESULT CAdapRegPerf::ProcessLibrary( WCHAR* pwcsServiceName, BOOL bDelta ) /////////////////////////////////////////////////////////////////////////////// // // Once a performance library has been discovered, then it's schema must be // retrieved and the performance library's class list compared to what is // already in the WMI repository. The comparison is achieved in the "Merge" // method of the master class list which extracts any classes from the perf // lib's class list that are not already in the master class list. The // comparison occurs for both the raw and the cooked classes. // // Parameters: // pwcsServiceName - The name of the service to be processed // /////////////////////////////////////////////////////////////////////////////// { HRESULT hr = WBEM_NO_ERROR; try { // Add the name of the performance library to the perflib list // =========================================================== // The list is used for book keeping purposses to track processing // in the event of a perflib failure g_PerfLibList.AddPerfLib( pwcsServiceName ); // Construct and initialize the schema for the perflib // =================================================== DWORD LoadStatus = EX_STATUS_UNLOADED; CPerfLibSchema Schema( pwcsServiceName, m_pLocaleCache ); hr = Schema.Initialize( bDelta, &LoadStatus); DEBUGTRACE(( LOG_WMIADAP,"CPerfLibSchema::Initialize for %S hr %08x\n",pwcsServiceName,hr)); if ( !bDelta || ( bDelta && ( hr != WBEM_S_ALREADY_EXISTS ) ) ) { // Update raw and cooked classes // ============================= for ( DWORD dwType = 0; ( dwType < WMI_ADAP_NUM_TYPES ) && SUCCEEDED( hr ); dwType++ ) { // Get the class list for classes from the perflib's schema // ======================================================== CClassList* pClassList = NULL; hr = Schema.GetClassList( dwType, &pClassList ); CAdapReleaseMe rmClassList( pClassList ); DEBUGTRACE(( LOG_WMIADAP,"GetClassList for %S hr %08x\n",pwcsServiceName,hr)); if ( SUCCEEDED( hr ) ) { // Merge the raw classes obtained from the perflib into the master class list // ========================================================================== hr = m_apMasterClassList[dwType]->Merge( pClassList, bDelta ); DEBUGTRACE(( LOG_WMIADAP,"m_apMasterClassList[%d]->Merge for %S hr %08x\n",dwType,pwcsServiceName,hr)); } //if (bDelta && FAILED(hr)){ // // the class was not in the repository if we are here // LoadStatus = EX_STATUS_UNLOADED; //} } }; if (FAILED(hr) && (LoadStatus != EX_STATUS_LOADABLE)) { for ( DWORD dwType = 0; ( dwType < WMI_ADAP_NUM_TYPES ) ; dwType++ ) { DEBUGTRACE((LOG_WMIADAP,"ProcessLibrary ForceStatus for %S hr = %08x\n",pwcsServiceName,hr)); DWORD NewStatus = ADAP_OBJECT_IS_DELETED; if (LoadStatus == EX_STATUS_UNLOADED) { NewStatus |= ADAP_OBJECT_IS_TO_BE_CLEARED; } m_apMasterClassList[dwType]->ForceStatus(pwcsServiceName,TRUE,NewStatus); } } } catch(...) { hr = WBEM_E_OUT_OF_MEMORY; } return hr; } unsigned int CAdapRegPerf::GoGershwin( void* pParam ) /////////////////////////////////////////////////////////////////////////////// // // The monitoring thread entry point // /////////////////////////////////////////////////////////////////////////////// { HRESULT hr = WBEM_S_NO_ERROR; try { CAdapRegPerf* pThis = (CAdapRegPerf*)pParam; HANDLE ahHandles[2]; // If we don't have an initialized PID, then find one from WMI // =========================================================== if ( 0 == pThis->m_dwPID ) { pThis->m_dwPID = GetExecPid(); } // Get the process handle and wait for a signal // ============================================ if ( SUCCEEDED( hr ) && ( 0 != pThis->m_dwPID ) ) { ahHandles[0] = OpenProcess( SYNCHRONIZE, FALSE, pThis->m_dwPID ); CCloseMe cmProcess( ahHandles[0] ); ahHandles[1] = pThis->m_hTerminationEvent; DWORD dwRet = WaitForMultipleObjects( 2, ahHandles, FALSE, INFINITE ); switch ( dwRet ) { case WAIT_FAILED: // Something is wierd case WAIT_OBJECT_0: // The service process { pThis->m_fQuit = TRUE; // Set the termination flag } break; case ( WAIT_OBJECT_0 + 1 ): // The completion event { // continue }break; } } } catch(...) { // We have been betrayed... try to write something to the error log // ======================================================================= CriticalFailADAPTrace( "An unhandled exception has been thrown in the WMI monitoring thread." ); } return 0; } HRESULT CAdapRegPerf::GetADAPStatusObject( void ) { IWbemLocator* pLocator = NULL; HRESULT hr = CoCreateInstance( CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (void**) &pLocator ); CReleaseMe rm( pLocator ); if ( SUCCEEDED( hr ) ) { BSTR bstrNameSpace = SysAllocString( L"root\\default" ); BSTR bstrInstancePath = SysAllocString( L"__ADAPStatus=@" ); CSysFreeMe sfm1( bstrNameSpace ); CSysFreeMe sfm2( bstrInstancePath ); if ( NULL != bstrNameSpace && NULL != bstrInstancePath ) { // Connect to Root\default and get hold of the status object hr = pLocator->ConnectServer( bstrNameSpace, // NameSpace Name NULL, // UserName NULL, // Password NULL, // Locale 0L, // Security Flags NULL, // Authority NULL, // Wbem Context &m_pRootDefault // Namespace ); if ( SUCCEEDED( hr ) ) { // Set Interface security hr = WbemSetProxyBlanket( m_pRootDefault, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_PKT,RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); if ( SUCCEEDED( hr ) ) { hr = m_pRootDefault->GetObject( bstrInstancePath, 0L, NULL, &m_pADAPStatus, NULL ); if ( SUCCEEDED( hr ) ) { SetADAPStatus( eADAPStatusRunning ); } } } } else { hr = WBEM_E_OUT_OF_MEMORY; } } // IF got locator return hr; } // Gets the time in the popular DMTF format void CAdapRegPerf::GetTime( LPWSTR Buff, size_t cchBuffSize ) { SYSTEMTIME st; int Bias=0; char cOffsetSign = '+'; GetLocalTime( &st ); TIME_ZONE_INFORMATION ZoneInformation; DWORD dwRet = GetTimeZoneInformation(&ZoneInformation); if(dwRet != TIME_ZONE_ID_UNKNOWN) Bias = -ZoneInformation.Bias; if(Bias < 0) { cOffsetSign = '-'; Bias = -Bias; } StringCchPrintfW(Buff,cchBuffSize, L"%4d%02d%02d%02d%02d%02d.%06d%c%03d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds*1000, cOffsetSign, Bias); } // Sets the status back in WMI void CAdapRegPerf::SetADAPStatus( eADAPStatus status ) { HRESULT hr = E_FAIL; // Make sure we've got both our pointers if ( NULL != m_pRootDefault && NULL != m_pADAPStatus ) { // We only need 25 characters for this WCHAR wcsTime[32]; _variant_t var; // legacy fastprox behavior WCHAR pNum[16]; StringCchPrintfW(pNum,16,L"%u",status); var = pNum; hr = m_pADAPStatus->Put( L"Status", 0L, &var, 0 );//CIM_UINT32 ); if ( SUCCEEDED( hr ) ) { // Set the time property if necessary if ( status == eADAPStatusRunning || status == eADAPStatusFinished ) { GetTime( wcsTime, 32 ); // This can fail try { var = wcsTime; } catch(...) { hr = WBEM_E_OUT_OF_MEMORY; } if ( SUCCEEDED( hr ) ) { hr = ( status == eADAPStatusRunning ? m_pADAPStatus->Put( L"LastStartTime", 0L, &var, CIM_DATETIME ) : m_pADAPStatus->Put( L"LastStopTime", 0L, &var, CIM_DATETIME ) ); } } if ( SUCCEEDED( hr ) ) { hr = m_pRootDefault->PutInstance( m_pADAPStatus, 0L, NULL, NULL ); } } // Set the Status property } // Make sure we've got both pointers }