/****************************************************************************** Copyright (c) 1999 Microsoft Corporation Module Name: SystemMonitor.cpp Abstract: This file contains the implementation of the CPCHSystemMonitor class, which implements the data collection functionality. Revision History: Davide Massarenti (Dmassare) 08/25/99 created ******************************************************************************/ #include "stdafx.h" #include #include // for task scheduler apis #include ///////////////////////////////////////////////////////////////////////////// static const ULONG INITIAL_RESCHEDULE_TIME = 10 * 60; // (10 minutes) static const ULONG DATACOLLECTION_RESCHEDULE_TIME = 6 * 60 * 60; // (6 hours) static const ULONG DATACOLLECTION_IDLE_TIME = 5; // (5 minutes) static const ULONG SECONDS_IN_A_DAY = 24 * 60 * 60; static const ULONG SECONDS_IN_A_MINUTE = 60; static const ULONG MINUTES_IN_A_DAY = 24 * 60; ///////////////////////////////////////////////////////////////////////////// static HRESULT Exec( LPCWSTR szExec ) { MPC::wstring strCmdLine( szExec ); MPC::SubstituteEnvVariables( strCmdLine ); return MPC::ExecuteCommand( strCmdLine ); } ///////////////////////////////////////////////////////////////////////////// CPCHSystemMonitor::CPCHSystemMonitor() { __HCP_FUNC_ENTRY( "CPCHSystemMonitor::CPCHSystemMonitor" ); m_fLoadCache = false; // bool m_fLoadCache; m_fScanBatch = true; // bool m_fScanBatch; m_fDataCollection = false; // bool m_fDataCollection; (void)MPC::_MPC_Module.RegisterCallback( this, (void (CPCHSystemMonitor::*)())Shutdown ); } CPCHSystemMonitor::~CPCHSystemMonitor() { MPC::_MPC_Module.UnregisterCallback( this ); Shutdown(); } //////////////////// CPCHSystemMonitor* CPCHSystemMonitor::s_GLOBAL( NULL ); HRESULT CPCHSystemMonitor::InitializeSystem() { if(s_GLOBAL == NULL) { s_GLOBAL = new CPCHSystemMonitor; } return s_GLOBAL ? S_OK : E_OUTOFMEMORY; } void CPCHSystemMonitor::FinalizeSystem() { if(s_GLOBAL) { delete s_GLOBAL; s_GLOBAL = NULL; } } //////////////////// void CPCHSystemMonitor::Shutdown() { Thread_Wait(); } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT CPCHSystemMonitor::EnsureStarted() { __HCP_FUNC_ENTRY( "CPCHSystemMonitor::EnsureStarted" ); HRESULT hr; if(Thread_IsRunning() == false && Thread_IsAborted() == false ) { __MPC_EXIT_IF_METHOD_FAILS(hr, Thread_Start( this, Run, NULL )); } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT CPCHSystemMonitor::Run() { __HCP_FUNC_ENTRY( "CPCHSystemMonitor::Run" ); const DWORD s_dwNotify = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_CREATION; HRESULT hr; MPC::wstring strBatch( HC_ROOT_HELPSVC_BATCH ); MPC::SubstituteEnvVariables( strBatch ); HANDLE hBatchNotification = INVALID_HANDLE_VALUE; DWORD dwTimeout = INFINITE; MPC::SmartLock<_ThreadModel> lock( this ); ::SetThreadPriority( ::GetCurrentThread(), THREAD_PRIORITY_LOWEST ); ::Sleep( 0 ); // Yield processor... //// //// Don't touch task scheduler, it brings in too many things... //// //// // //// // Move forward the scheduled data collection by at least 10 minutes. //// // Task scheduler is available only on normal boots. //// // //// if(::GetSystemMetrics( SM_CLEANBOOT ) == 0) //// { //// __MPC_EXIT_IF_METHOD_FAILS(hr, TaskScheduler_Add( true )); //// } hBatchNotification = ::FindFirstChangeNotificationW( strBatch.c_str(), TRUE, s_dwNotify ); while(Thread_IsAborted() == false) { DWORD dwRes; lock = NULL; __MPC_EXIT_IF_METHOD_FAILS(hr, RunLoop()); lock = this; // // Done, for now... // lock = NULL; dwRes = Thread_WaitForEvents( hBatchNotification == INVALID_HANDLE_VALUE ? NULL : hBatchNotification, dwTimeout ); lock = this; switch(dwRes) { case WAIT_OBJECT_0 : case WAIT_ABANDONED_0: break; case WAIT_OBJECT_0 + 1: case WAIT_ABANDONED_0 + 1: ::FindNextChangeNotification( hBatchNotification ); dwTimeout = 1*1000; // Don't scan immediately, wait some time. break; case WAIT_TIMEOUT: dwTimeout = INFINITE; m_fScanBatch = true; break; case WAIT_FAILED: __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ::GetLastError()); } } hr = S_OK; __HCP_FUNC_CLEANUP; if(hBatchNotification != INVALID_HANDLE_VALUE) { ::FindCloseChangeNotification( hBatchNotification ); } Thread_Abort (); // To tell the MPC:Thread object to close the worker thread... Thread_Release(); // To tell the MPC:Thread object to clean up... __HCP_FUNC_EXIT(hr); } HRESULT CPCHSystemMonitor::RunLoop() { __HCP_FUNC_ENTRY( "CPCHSystemMonitor::RunLoop" ); HRESULT hr; //////////////////////////////////////////////////////////////////////////////// // // Batch processing for the other update packages. // if(m_fScanBatch) { CComPtr sht; m_fScanBatch = false; __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &sht )); __MPC_EXIT_IF_METHOD_FAILS(hr, sht->ScanBatch()); } //////////////////////////////////////////////////////////////////////////////// // // Load the cache for active SKUs. // if(m_fLoadCache) { Taxonomy::LockingHandle handle; Taxonomy::InstalledInstanceIterConst itBegin; Taxonomy::InstalledInstanceIterConst itEnd; Taxonomy::InstalledInstanceIterConst it; m_fLoadCache = false; // // Get the list of SKU installed on the machine. // __MPC_EXIT_IF_METHOD_FAILS(hr, Taxonomy::InstalledInstanceStore::s_GLOBAL->GrabControl( handle )); __MPC_EXIT_IF_METHOD_FAILS(hr, Taxonomy::InstalledInstanceStore::s_GLOBAL->SKU_GetList( itBegin, itEnd )); // // Enumerate all of the SKUs, creating the index. // for(it = itBegin; it != itEnd; it++) { __MPC_EXIT_IF_METHOD_FAILS(hr, Taxonomy::Cache::s_GLOBAL->LoadIfMarked( it->m_inst.m_ths )); } } if(m_fDataCollection) { CComPtr pdc; m_fDataCollection = false; __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &pdc )); __MPC_EXIT_IF_METHOD_FAILS(hr, pdc->ExecScheduledCollection()); __MPC_EXIT_IF_METHOD_FAILS(hr, TaskScheduler_Add( false )); } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////////////////////////////////////////////// HRESULT CPCHSystemMonitor::LoadCache() { __HCP_FUNC_ENTRY( "CPCHSystemMonitor::LoadCache" ); HRESULT hr; MPC::SmartLock<_ThreadModel> lock( this ); __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureStarted()); m_fLoadCache = true; Thread_Signal(); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT CPCHSystemMonitor::TriggerDataCollection( /*[in]*/ bool fStart ) { __HCP_FUNC_ENTRY( "CPCHSystemMonitor::TriggerDataCollection" ); HRESULT hr; MPC::SmartLock<_ThreadModel> lock( this ); if(fStart) { __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureStarted()); m_fDataCollection = true; Thread_Signal(); } else { m_fDataCollection = false; Thread_Signal(); } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } ///////////////////////////////////////////////////////////////////////////// HRESULT CPCHSystemMonitor::TaskScheduler_Add( /*[in]*/ bool fAfterBoot ) { MPC::wstring strDate; if(SUCCEEDED(MPC::ConvertDateToString( MPC::GetLocalTime(), strDate, /*fGMT*/false, /*fCIM*/true, 0 ))) { static const WCHAR s_szRoot [] = HC_REGISTRY_PCHSVC; static const WCHAR s_szDataCollection[] = L"DataCollection"; (void)MPC::RegKey_Value_Write( strDate, s_szRoot, s_szDataCollection ); } return S_OK; } HRESULT CPCHSystemMonitor::TaskScheduler_Remove() { return S_OK; } ////HRESULT CPCHSystemMonitor::TaskScheduler_Add( /*[in]*/ bool fAfterBoot ) ////{ //// __HCP_FUNC_ENTRY( "CPCHSystemMonitor::TaskScheduler_Add" ); //// //// HRESULT hr; //// CComPtr pTaskScheduler; //// CComPtr pTask; //// CComPtr pTaskUnknown; //// CComPtr pScheduledWorkItem; //// bool fTaskExists = false; //// //// WCHAR rgFileName[MAX_PATH]; //// CComBSTR bstrTaskName; //// CComBSTR bstrComments; //// //// ULONG ulTime = fAfterBoot ? INITIAL_RESCHEDULE_TIME : DATACOLLECTION_RESCHEDULE_TIME; //// WORD wIdle = DATACOLLECTION_IDLE_TIME; //// //// //////////////////////////////////////// //// //// // //// // Get our complete filename -- needed to create a task in the task scheduler. //// // //// __MPC_EXIT_IF_CALL_RETURNS_ZERO(hr, ::GetModuleFileNameW( NULL, rgFileName, MAX_PATH )); //// //// //// __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::LocalizeString( IDS_HELPSVC_TASKNAME , bstrTaskName )); //// __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::LocalizeString( IDS_HELPSVC_TASKCOMMENT, bstrComments )); //// //// // //// // First create the task scheduler. //// // //// __MPC_EXIT_IF_METHOD_FAILS(hr, ::CoCreateInstance( CLSID_CTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskScheduler, (void**)&pTaskScheduler )); //// //// //// // //// // See if the task already exists in the task scheduler //// // //// if(SUCCEEDED(pTaskScheduler->Activate( bstrTaskName, IID_ITask, &pTaskUnknown ))) //// { //// fTaskExists = true; //// //// __MPC_EXIT_IF_METHOD_FAILS(hr, pTaskUnknown->QueryInterface( IID_ITask, (void **)&pTask )); //// } //// else //// { //// // //// // Create a new task and set its app name and parameters. //// // //// if(FAILED(hr = pTaskScheduler->NewWorkItem( bstrTaskName, CLSID_CTask, IID_ITask, (IUnknown**)&pTask ))) //// { //// if(hr != ERROR_FILE_EXISTS) //// { //// __MPC_TRACE_HRESULT(hr); //// __MPC_FUNC_LEAVE; //// } //// } //// } //// //// __MPC_EXIT_IF_METHOD_FAILS(hr, pTask->QueryInterface( IID_IScheduledWorkItem, (void **)&pScheduledWorkItem )); //// //// // //// // Run under SYSTEM. //// // //// __MPC_EXIT_IF_METHOD_FAILS(hr, pScheduledWorkItem->SetAccountInformation( L"", NULL )); //// //// __MPC_EXIT_IF_METHOD_FAILS(hr, pTask->SetApplicationName( CComBSTR( rgFileName ) )); //// __MPC_EXIT_IF_METHOD_FAILS(hr, pTask->SetParameters ( CComBSTR( L"-collect" ) )); //// //// // Set the comment, so we know how this job go there. //// __MPC_EXIT_IF_METHOD_FAILS(hr, pScheduledWorkItem->SetComment( bstrComments )); //// __MPC_EXIT_IF_METHOD_FAILS(hr, pScheduledWorkItem->SetFlags ( 0 )); //// //// //// //// //// // Now, fill in the trigger as necessary. //// { //// CComPtr pTaskTrigger; //// SYSTEMTIME stNow; //// DOUBLE dblNextScheduledTime; //// TASK_TRIGGER ttTaskTrig; //// //// //// ::ZeroMemory( &ttTaskTrig, sizeof(ttTaskTrig) ); //// ttTaskTrig.cbTriggerSize = sizeof(ttTaskTrig); //// //// //// if(fTaskExists) //// { //// __MPC_EXIT_IF_METHOD_FAILS(hr, pScheduledWorkItem->GetTrigger( 0, &pTaskTrigger )); //// __MPC_EXIT_IF_METHOD_FAILS(hr, pTaskTrigger ->GetTrigger( &ttTaskTrig )); //// } //// else //// { //// WORD wTrigNumber; //// //// __MPC_EXIT_IF_METHOD_FAILS(hr, pScheduledWorkItem->CreateTrigger( &wTrigNumber, &pTaskTrigger )); //// } //// //// //// // //// // Calculate the exact time of next activation. //// // //// ::GetLocalTime ( &stNow ); //// ::SystemTimeToVariantTime( &stNow, &dblNextScheduledTime ); //// //// dblNextScheduledTime += (double)ulTime / SECONDS_IN_A_DAY; //// ::VariantTimeToSystemTime( dblNextScheduledTime, &stNow ); //// //// //// ttTaskTrig.wBeginYear = stNow.wYear; //// ttTaskTrig.wBeginMonth = stNow.wMonth; //// ttTaskTrig.wBeginDay = stNow.wDay; //// ttTaskTrig.wStartHour = stNow.wHour; //// ttTaskTrig.wStartMinute = stNow.wMinute; //// //// ttTaskTrig.MinutesDuration = MINUTES_IN_A_DAY; //// ttTaskTrig.MinutesInterval = ulTime / SECONDS_IN_A_MINUTE; //// ttTaskTrig.TriggerType = TASK_TIME_TRIGGER_DAILY; //// //// ttTaskTrig.Type.Daily.DaysInterval = 1; //// //// if(wIdle) //// { //// __MPC_EXIT_IF_METHOD_FAILS(hr, pScheduledWorkItem->SetIdleWait( wIdle, 0x7FFF )); //// __MPC_EXIT_IF_METHOD_FAILS(hr, pScheduledWorkItem->SetFlags ( TASK_FLAG_START_ONLY_IF_IDLE )); //// } //// //// // Add this trigger to the task. //// __MPC_EXIT_IF_METHOD_FAILS(hr, pTaskTrigger->SetTrigger( &ttTaskTrig )); //// } //// //// // //// // Make the changes permanent. //// // //// { //// CComPtr pIPF; //// //// __MPC_EXIT_IF_METHOD_FAILS(hr, pTask->QueryInterface( IID_IPersistFile, (void **)&pIPF )); //// //// __MPC_EXIT_IF_METHOD_FAILS(hr, pIPF->Save( NULL, FALSE )); //// } //// //// //// hr = S_OK; //// //// //// __HCP_FUNC_CLEANUP; //// //// __HCP_FUNC_EXIT(hr); ////} //// ////HRESULT CPCHSystemMonitor::TaskScheduler_Remove() ////{ //// __HCP_FUNC_ENTRY( "CPCHSystemMonitor::TaskScheduler_Remove" ); //// //// HRESULT hr; //// CComPtr pTaskScheduler; //// CComBSTR bstrTaskName; //// //// //// //////////////////////////////////////// //// //// __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::LocalizeString( IDS_HELPSVC_TASKNAME, bstrTaskName )); //// //// // //// // First create the task scheduler. //// // //// __MPC_EXIT_IF_METHOD_FAILS(hr, ::CoCreateInstance( CLSID_CTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskScheduler, (void**)&pTaskScheduler )); //// //// //// if(FAILED(hr = pTaskScheduler->Delete( bstrTaskName ))) //// { //// if(hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) //// { //// __MPC_TRACE_HRESULT(hr); //// __MPC_FUNC_LEAVE; //// } //// } //// //// //// hr = S_OK; //// //// //// __HCP_FUNC_CLEANUP; //// //// __HCP_FUNC_EXIT(hr); ////} //////////////////////////////////////////////////////////////////////////////// HRESULT CPCHSystemMonitor::Startup() { __HCP_FUNC_ENTRY( "CPCHSystemMonitor::Startup" ); HRESULT hr; // // This forces the Content Store to be loaded. // __MPC_EXIT_IF_METHOD_FAILS(hr, CPCHContentStore::s_GLOBAL->Acquire()); __MPC_EXIT_IF_METHOD_FAILS(hr, CPCHContentStore::s_GLOBAL->Release()); //////////////////////////////////////////////////////////////////////////////// // // Force the loading of the cache. // { Taxonomy::LockingHandle handle; __MPC_EXIT_IF_METHOD_FAILS(hr, Taxonomy::InstalledInstanceStore::s_GLOBAL->GrabControl( handle )); } __MPC_EXIT_IF_METHOD_FAILS(hr, EnsureStarted()); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); }