|
|
/*++
Copyright (C) 1999-2001 Microsoft Corporation
Module Name:
RESYNC.CPP
Abstract:
Implements the windows application or an NT service which loads up the various transport prtocols.
If started with /exe argument, it will always run as an exe. If started with /kill argument, it will stop any running exes or services. If started with /? or /help dumps out information.
History:
a-davj 04-Mar-97 Created.
--*/
#include "precomp.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <malloc.h>
#include <tchar.h>
#include <reg.h>
#include <wbemutil.h>
#include <cntserv.h>
#include <sync.h>
#include <winntsec.h>
#include <wbemidl.h>
#include <cominit.h>
#include <wbemint.h>
#include <wbemprov.h>
#include <winmgmtr.h>
#include <genutils.h>
#include "WinMgmt.h"
#include "adapreg.h"
#include "adaputil.h"
#include "process.h"
#include "resync.h"
// Timeout is a 64-bit value. See documentation on SetWaitableTimer
// for why we are setting it this way.
#define _SECOND 10000000
#define RESYNC_TIMEOUT_INTERVAL 10 * _SECOND
#define WMIADAP_DEFAULT_DELAY 10
BOOL gfResyncInit = FALSE; HANDLE ghWaitableTimer = NULL; BOOL gfSpawnedResync = FALSE; DWORD gdwADAPDelaySec = 0;
HANDLE ghResyncThreadHandle = NULL; HANDLE ghResyncThreadEvent = NULL; CRITICAL_SECTION* g_pResyncCs = NULL; DWORD gdwResyncThreadId = 0;
// A global handle used to store the last dredger we
// kicked off!
HANDLE ghChildProcessHandle = NULL;
PCREATEWAITABLETIMERW gpCreateWaitableTimerW = NULL; PSETWAITABLETIMER gpSetWaitableTimerW = NULL; HINSTANCE ghKernel32;
class CAutoFreeLib { public: ~CAutoFreeLib() { if ( NULL != ghKernel32 ) FreeLibrary( ghKernel32); } };
void ResetResyncTimer( HANDLE hResyncTimer ) { DWORD dwErr = 0; __int64 qwDueTime = gdwADAPDelaySec * _SECOND; // RESYNC_TIMEOUT_INTERVAL;
// Convert it to relative time
qwDueTime *= -1;
// Copy the relative time into a LARGE_INTEGER.
LARGE_INTEGER li;
li.LowPart = (DWORD) ( qwDueTime & 0xFFFFFFFF ); li.HighPart = (LONG) ( qwDueTime >> 32 );
if ( !gpSetWaitableTimerW( hResyncTimer, &li, 0, NULL, NULL, FALSE ) ) { dwErr = GetLastError(); }
}
// This thread controls the actual shelling of a resync perf operation
unsigned __stdcall ResyncPerfThread( void* pVoid ) { RESYNCPERFDATASTRUCT* pResyncPerfData = (RESYNCPERFDATASTRUCT*) pVoid;
// We get the two handles, copy them and wait on them
// The first handle is the terminate event, the second is the
// timer on which to spin off the resync
HANDLE aHandles[2];
aHandles[0] = pResyncPerfData->m_hTerminate; HANDLE hTimer = pResyncPerfData->m_hWaitableTimer;
CRITICAL_SECTION* pcs = pResyncPerfData->m_pcs;
delete pResyncPerfData; pResyncPerfData = NULL;
// Reset the spawned flag
gfSpawnedResync = FALSE;
// Okay. Signal this event so the starting thread can get us going
SetEvent( ghResyncThreadEvent );
// Now, if ghChildProcessHandle is not NULL, then we've obviously kicked off a
// dredge before. See where the last one is at. If it's not done, wait for
// it to finish. We will always check this at the start of this chunk of code,
// since we are really the only location in which the process handle can ever get set,
// and there really shouldn't be more than one thread ever, waiting to start another
// dredge
if ( NULL != ghChildProcessHandle ) {
aHandles[1] = ghChildProcessHandle;
DWORD dwWait = WaitForMultipleObjects( 2, aHandles, FALSE, INFINITE );
// If abort was signalled, leave!
if ( dwWait == WAIT_OBJECT_0 ) { return 0; }
// If the process handle was signalled, close the process, reset the timer
// and we'll get ready to start the next dredge!
if ( dwWait == WAIT_OBJECT_0 + 1 ) { EnterCriticalSection( pcs );
CloseHandle( ghChildProcessHandle ); ghChildProcessHandle = NULL; ResetResyncTimer( hTimer );
LeaveCriticalSection( pcs );
}
} else { // If the Child Process Handle is NULL, we've never dredged before, so we'll
// just reset the timer
ResetResyncTimer( hTimer ); }
BOOL fHoldOff = TRUE;
// Reset this handle to the timer now
aHandles[1] = hTimer;
while ( fHoldOff ) { // Wait for either the terminate event or the timer
DWORD dwWait = WaitForMultipleObjects( 2, aHandles, FALSE, INFINITE );
// This means the timer was signaled
if ( dwWait == WAIT_OBJECT_0 + 1 ) { // Quick sanity check on the abort event
if ( WaitForSingleObject( aHandles[0], 0 ) == WAIT_OBJECT_0 ) { // Outa here!
break; }
EnterCriticalSection( pcs );
// Finally, if the current thread id != gdwResyncThreadId, this means another
// resync perf thread got kicked off, inside of the critical section,
// so we should just let it wait on the timer. We don't really need to do
// this, since the main thread will wait on this thread to complete before
// it actually kicks off another thread.
if ( GetCurrentThreadId() != gdwResyncThreadId ) { // Used the following int 3 for debugging
// _asm int 3;
LeaveCriticalSection( pcs ); break; }
// Once we get through the critical section, check that the
// timer is still signalled. If it is not, this means that somebody
// got control of the critical section and reset the timer
if ( WaitForSingleObject( aHandles[1], 0 ) == WAIT_OBJECT_0 ) {
// Last quick sanity check on the abort event
if ( WaitForSingleObject( aHandles[0], 0 ) == WAIT_OBJECT_0 ) { // Outa here!
LeaveCriticalSection( pcs ); break; }
// Okay, we really will try to create the process now.
gfSpawnedResync = TRUE;
// We signalled to start the process, so make it so.
PROCESS_INFORMATION pi; STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
TCHAR szPath[MAX_PATH+1]; GetModuleFileName(NULL, szPath, MAX_PATH);
TCHAR szCmdLine[256];
_stprintf(szCmdLine, __TEXT("WINMGMT.EXE -RESYNCPERF %d"), _getpid());
BOOL bRes = CreateProcess(szPath, szCmdLine, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi); if(bRes) { // Who cares about this one?
CloseHandle(pi.hThread);
// Clean up our old values
if ( NULL != ghChildProcessHandle ) { CloseHandle( ghChildProcessHandle ); ghChildProcessHandle = NULL; }
ghChildProcessHandle = pi.hProcess; }
// We're done
fHoldOff = FALSE;
} // Check that we're still signalled, or we will just have to go back to waiting
LeaveCriticalSection( pcs );
} // IF timer was signalled
} // WHILE fHoldOff
return 0; }
// For the waitable timer
//#define _SECOND 10000000
// Create all the things we need
BOOL InitResync( void ) { if ( gfResyncInit ) return gfResyncInit;
if ( ( NULL == gpCreateWaitableTimerW ) && ( NULL == gpSetWaitableTimerW ) ) { ghKernel32 = LoadLibrary( __TEXT("Kernel32.dll") ); if ( NULL == ghKernel32 ) { return FALSE; } gpCreateWaitableTimerW = ( PCREATEWAITABLETIMERW ) GetProcAddress( ghKernel32, "CreateWaitableTimerW" ); gpSetWaitableTimerW = ( PSETWAITABLETIMER ) GetProcAddress( ghKernel32, "SetWaitableTimer" );
if ( ( NULL == gpCreateWaitableTimerW ) || ( NULL == gpSetWaitableTimerW ) ) { FreeLibrary( ghKernel32 ); ghKernel32 = NULL; return FALSE; } } if ( NULL == ghWaitableTimer ) { ghWaitableTimer = gpCreateWaitableTimerW( NULL, TRUE, NULL );
// We gotta big problem
if ( NULL == ghWaitableTimer ) { // Log an error here
ERRORTRACE( ( LOG_WINMGMT, "Could not create a waitable timer for Resyncperf.\n" ) ); }
}
if ( NULL == ghResyncThreadEvent ) { ghResyncThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
// We gotta big problem
if ( NULL == ghResyncThreadEvent ) { // Log an event here
ERRORTRACE( ( LOG_WINMGMT, "Could not create a ResyncThreadEvent event for Resyncperf.\n" ) ); }
}
// This critical section won't be freed or deleted because of
// potential timing issues. But since it's only one, I think
// we can live with it.
if ( NULL == g_pResyncCs ) { g_pResyncCs = new CRITICAL_SECTION;
// We gotta big problem
if ( NULL == g_pResyncCs ) { // Log an event here
ERRORTRACE( ( LOG_WINMGMT, "Could not create a ResyncCs critical section for Resyncperf.\n" ) ); } else { InitializeCriticalSection( g_pResyncCs ); }
}
gfResyncInit = ( NULL != ghWaitableTimer && NULL != g_pResyncCs && NULL != ghResyncThreadEvent );
// Read the initialization information
CNTRegistry reg; if ( CNTRegistry::no_error == reg.Open( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\WBEM\\CIMOM" ) ) { long lError = reg.GetDWORD( L"ADAPDelay", &gdwADAPDelaySec );
if ( CNTRegistry::no_error == lError ) { //This is what we want
} else if ( CNTRegistry::not_found == lError ) { // Not set, so add it
reg.SetDWORD( L"ADAPDelay", WMIADAP_DEFAULT_DELAY ); gdwADAPDelaySec = WMIADAP_DEFAULT_DELAY; } else { // Error
ERRORTRACE( ( LOG_WINMGMT, "ResyncPerf experienced an error while attempting to read the WMIADAPDelay value in the CIMOM subkey. Continuing using a default value.\n" ) ); gdwADAPDelaySec = WMIADAP_DEFAULT_DELAY; } } else { // Error
ERRORTRACE( ( LOG_WINMGMT, "ResyncPerf could not open the CIMOM subkey to read initialization data. Continuing using a default value.\n" ) ); gdwADAPDelaySec = WMIADAP_DEFAULT_DELAY; }
return gfResyncInit; }
// PLEASE NOTE - THIS FUNCTION IS NOT REENTRANT! PLEASE DO NOT CALL IT ON MULTIPLE THREADS!
void ResyncPerf( HANDLE hTerminate ) { // Make sure this is Win2000 or greater
if ( !IsW2KOrMore() ) { return; }
// Assume that we should check the timer
BOOL fFirstTime = !gfResyncInit;
if ( !InitResync() ) return;
// Auto FreeLibrary for the gpKernel32 Library handle
CAutoFreeLib aflKernel32; EnterCriticalSection( g_pResyncCs );
// Now, if this or the first time, or the spawned resyncflag is set to TRUE, then we need
// to kick off another thread. By checking gfSpawnedResync in a critical section, since
// it only gets set in the same critical section, we ensure that we will resignal as needed
// as well as only kick off a thread when we really need to.
BOOL fSpawnThread = ( fFirstTime || gfSpawnedResync );
if ( !fSpawnThread ) { // We are here because we don't appear to have spawned a resync.
// This is either because we are servicing many lodctr requests
// within our time delay, or a dredger was started and
// a previous request request to dredge is waiting for
// the process to complete. If the child process handle
// is not NULL, there is no real need to reset the
// waitable timer
if ( NULL == ghChildProcessHandle && ghResyncThreadHandle ) { // Reset the timer here
ResetResyncTimer( ghWaitableTimer ); }
}
LeaveCriticalSection( g_pResyncCs );
if ( fSpawnThread ) { HANDLE ahHandle[2];
if ( NULL != ghResyncThreadHandle ) { ahHandle[0] = hTerminate; ahHandle[1] = ghResyncThreadHandle;
// Wait for ten seconds on this handle. If it is not signalled, something is
// direly wrong. We're probably not going to be able to kick off a dredge
// so put some info to this effect in the error log. The only time we should
// have contention here, is when a lodctr event is signalled, just as the timer
// becomes signalled. The resync thread will wake up and start another dredge
// this thread will wait for the other thread to complete before continuing.
// We will kick off another resync thread, which will start another dredge,
// but it will wait for the first dredge to continue. This is a worst case
// scenario, and arguably kicking off two dredges isn't that bad of a bailout
DWORD dwRet = WaitForMultipleObjects( 2, ahHandle, FALSE, 10000 );
// We're done
if ( dwRet == WAIT_OBJECT_0 ) { return; }
if ( dwRet != WAIT_OBJECT_0 + 1 ) { ERRORTRACE( ( LOG_WINMGMT, "The wait for a termination event or ResyncThreadHandle timed out in Resyncperf.\n" ) ); return; }
CloseHandle( ghResyncThreadHandle ); ghResyncThreadHandle = NULL; }
EnterCriticalSection( g_pResyncCs );
DWORD dwThreadId = 0;
RESYNCPERFDATASTRUCT* pResyncData = new RESYNCPERFDATASTRUCT;
// Boy are we low on memory!
if ( NULL == pResyncData ) { LeaveCriticalSection( g_pResyncCs );
// Log an event here
ERRORTRACE( ( LOG_WINMGMT, "Could not create a RESYNCPERFDATASTRUCT in Resyncperf.\n" ) ); return; }
// Store the data for the resync operation
pResyncData->m_hTerminate = hTerminate; pResyncData->m_hWaitableTimer = ghWaitableTimer; pResyncData->m_pcs = g_pResyncCs;
ghResyncThreadHandle = (HANDLE) _beginthreadex( NULL, 0, ResyncPerfThread, (void*) pResyncData, 0, (unsigned int *) &gdwResyncThreadId );
LeaveCriticalSection( g_pResyncCs );
if ( NULL == ghResyncThreadHandle ) { LeaveCriticalSection( g_pResyncCs );
// Log an event here
ERRORTRACE( ( LOG_WINMGMT, "Could not create a ResyncPerfThread thread in Resyncperf.\n" ) );
return; } else { // Wait for the resync thread event to be signalled by the thread we just started.
// If it doesn't signal in 10 seconds, something is VERY wrong
DWORD dwWait = WaitForSingleObject( ghResyncThreadEvent, INFINITE );
if ( dwWait != WAIT_OBJECT_0 ) { // Log an event
ERRORTRACE( ( LOG_WINMGMT, "The ResyncPerfThread thread never signaled the ghResyncThreadEvent in Resyncperf.\n" ) );
return; } }
} // IF fSpawnThread
}
|