Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

521 lines
16 KiB

  1. /*++
  2. Copyright (C) 1999-2001 Microsoft Corporation
  3. Module Name:
  4. RESYNC.CPP
  5. Abstract:
  6. Implements the windows application or an NT service which
  7. loads up the various transport prtocols.
  8. If started with /exe argument, it will always run as an exe.
  9. If started with /kill argument, it will stop any running exes or services.
  10. If started with /? or /help dumps out information.
  11. History:
  12. a-davj 04-Mar-97 Created.
  13. --*/
  14. #include "precomp.h"
  15. #include <stdio.h>
  16. #include <string.h>
  17. #include <stdlib.h>
  18. #include <time.h>
  19. #include <malloc.h>
  20. #include <tchar.h>
  21. #include <reg.h>
  22. #include <wbemutil.h>
  23. #include <cntserv.h>
  24. #include <sync.h>
  25. #include <winntsec.h>
  26. #include <wbemidl.h>
  27. #include <cominit.h>
  28. #include <wbemint.h>
  29. #include <wbemprov.h>
  30. #include <winmgmtr.h>
  31. #include <genutils.h>
  32. #include "WinMgmt.h"
  33. #include "adapreg.h"
  34. #include "adaputil.h"
  35. #include "process.h"
  36. #include "resync.h"
  37. // Timeout is a 64-bit value. See documentation on SetWaitableTimer
  38. // for why we are setting it this way.
  39. #define _SECOND 10000000
  40. #define RESYNC_TIMEOUT_INTERVAL 10 * _SECOND
  41. #define WMIADAP_DEFAULT_DELAY 10
  42. BOOL gfResyncInit = FALSE;
  43. HANDLE ghWaitableTimer = NULL;
  44. BOOL gfSpawnedResync = FALSE;
  45. DWORD gdwADAPDelaySec = 0;
  46. HANDLE ghResyncThreadHandle = NULL;
  47. HANDLE ghResyncThreadEvent = NULL;
  48. CRITICAL_SECTION* g_pResyncCs = NULL;
  49. DWORD gdwResyncThreadId = 0;
  50. // A global handle used to store the last dredger we
  51. // kicked off!
  52. HANDLE ghChildProcessHandle = NULL;
  53. PCREATEWAITABLETIMERW gpCreateWaitableTimerW = NULL;
  54. PSETWAITABLETIMER gpSetWaitableTimerW = NULL;
  55. HINSTANCE ghKernel32;
  56. class CAutoFreeLib
  57. {
  58. public:
  59. ~CAutoFreeLib() { if ( NULL != ghKernel32 ) FreeLibrary( ghKernel32); }
  60. };
  61. void ResetResyncTimer( HANDLE hResyncTimer )
  62. {
  63. DWORD dwErr = 0;
  64. __int64 qwDueTime = gdwADAPDelaySec * _SECOND; // RESYNC_TIMEOUT_INTERVAL;
  65. // Convert it to relative time
  66. qwDueTime *= -1;
  67. // Copy the relative time into a LARGE_INTEGER.
  68. LARGE_INTEGER li;
  69. li.LowPart = (DWORD) ( qwDueTime & 0xFFFFFFFF );
  70. li.HighPart = (LONG) ( qwDueTime >> 32 );
  71. if ( !gpSetWaitableTimerW( hResyncTimer, &li, 0, NULL, NULL, FALSE ) )
  72. {
  73. dwErr = GetLastError();
  74. }
  75. }
  76. // This thread controls the actual shelling of a resync perf operation
  77. unsigned __stdcall ResyncPerfThread( void* pVoid )
  78. {
  79. RESYNCPERFDATASTRUCT* pResyncPerfData = (RESYNCPERFDATASTRUCT*) pVoid;
  80. // We get the two handles, copy them and wait on them
  81. // The first handle is the terminate event, the second is the
  82. // timer on which to spin off the resync
  83. HANDLE aHandles[2];
  84. aHandles[0] = pResyncPerfData->m_hTerminate;
  85. HANDLE hTimer = pResyncPerfData->m_hWaitableTimer;
  86. CRITICAL_SECTION* pcs = pResyncPerfData->m_pcs;
  87. delete pResyncPerfData;
  88. pResyncPerfData = NULL;
  89. // Reset the spawned flag
  90. gfSpawnedResync = FALSE;
  91. // Okay. Signal this event so the starting thread can get us going
  92. SetEvent( ghResyncThreadEvent );
  93. // Now, if ghChildProcessHandle is not NULL, then we've obviously kicked off a
  94. // dredge before. See where the last one is at. If it's not done, wait for
  95. // it to finish. We will always check this at the start of this chunk of code,
  96. // since we are really the only location in which the process handle can ever get set,
  97. // and there really shouldn't be more than one thread ever, waiting to start another
  98. // dredge
  99. if ( NULL != ghChildProcessHandle )
  100. {
  101. aHandles[1] = ghChildProcessHandle;
  102. DWORD dwWait = WaitForMultipleObjects( 2, aHandles, FALSE, INFINITE );
  103. // If abort was signalled, leave!
  104. if ( dwWait == WAIT_OBJECT_0 )
  105. {
  106. return 0;
  107. }
  108. // If the process handle was signalled, close the process, reset the timer
  109. // and we'll get ready to start the next dredge!
  110. if ( dwWait == WAIT_OBJECT_0 + 1 )
  111. {
  112. EnterCriticalSection( pcs );
  113. CloseHandle( ghChildProcessHandle );
  114. ghChildProcessHandle = NULL;
  115. ResetResyncTimer( hTimer );
  116. LeaveCriticalSection( pcs );
  117. }
  118. }
  119. else
  120. {
  121. // If the Child Process Handle is NULL, we've never dredged before, so we'll
  122. // just reset the timer
  123. ResetResyncTimer( hTimer );
  124. }
  125. BOOL fHoldOff = TRUE;
  126. // Reset this handle to the timer now
  127. aHandles[1] = hTimer;
  128. while ( fHoldOff )
  129. {
  130. // Wait for either the terminate event or the timer
  131. DWORD dwWait = WaitForMultipleObjects( 2, aHandles, FALSE, INFINITE );
  132. // This means the timer was signaled
  133. if ( dwWait == WAIT_OBJECT_0 + 1 )
  134. {
  135. // Quick sanity check on the abort event
  136. if ( WaitForSingleObject( aHandles[0], 0 ) == WAIT_OBJECT_0 )
  137. {
  138. // Outa here!
  139. break;
  140. }
  141. EnterCriticalSection( pcs );
  142. // Finally, if the current thread id != gdwResyncThreadId, this means another
  143. // resync perf thread got kicked off, inside of the critical section,
  144. // so we should just let it wait on the timer. We don't really need to do
  145. // this, since the main thread will wait on this thread to complete before
  146. // it actually kicks off another thread.
  147. if ( GetCurrentThreadId() != gdwResyncThreadId )
  148. {
  149. // Used the following int 3 for debugging
  150. // _asm int 3;
  151. LeaveCriticalSection( pcs );
  152. break;
  153. }
  154. // Once we get through the critical section, check that the
  155. // timer is still signalled. If it is not, this means that somebody
  156. // got control of the critical section and reset the timer
  157. if ( WaitForSingleObject( aHandles[1], 0 ) == WAIT_OBJECT_0 )
  158. {
  159. // Last quick sanity check on the abort event
  160. if ( WaitForSingleObject( aHandles[0], 0 ) == WAIT_OBJECT_0 )
  161. {
  162. // Outa here!
  163. LeaveCriticalSection( pcs );
  164. break;
  165. }
  166. // Okay, we really will try to create the process now.
  167. gfSpawnedResync = TRUE;
  168. // We signalled to start the process, so make it so.
  169. PROCESS_INFORMATION pi;
  170. STARTUPINFO si;
  171. memset(&si, 0, sizeof(si));
  172. si.cb = sizeof(si);
  173. TCHAR szPath[MAX_PATH+1];
  174. GetModuleFileName(NULL, szPath, MAX_PATH);
  175. TCHAR szCmdLine[256];
  176. _stprintf(szCmdLine, __TEXT("WINMGMT.EXE -RESYNCPERF %d"), _getpid());
  177. BOOL bRes = CreateProcess(szPath, szCmdLine, NULL, NULL, FALSE, CREATE_NO_WINDOW,
  178. NULL, NULL, &si, &pi);
  179. if(bRes)
  180. {
  181. // Who cares about this one?
  182. CloseHandle(pi.hThread);
  183. // Clean up our old values
  184. if ( NULL != ghChildProcessHandle )
  185. {
  186. CloseHandle( ghChildProcessHandle );
  187. ghChildProcessHandle = NULL;
  188. }
  189. ghChildProcessHandle = pi.hProcess;
  190. }
  191. // We're done
  192. fHoldOff = FALSE;
  193. } // Check that we're still signalled, or we will just have to go back to waiting
  194. LeaveCriticalSection( pcs );
  195. } // IF timer was signalled
  196. } // WHILE fHoldOff
  197. return 0;
  198. }
  199. // For the waitable timer
  200. //#define _SECOND 10000000
  201. // Create all the things we need
  202. BOOL InitResync( void )
  203. {
  204. if ( gfResyncInit )
  205. return gfResyncInit;
  206. if ( ( NULL == gpCreateWaitableTimerW ) && ( NULL == gpSetWaitableTimerW ) )
  207. {
  208. ghKernel32 = LoadLibrary( __TEXT("Kernel32.dll") );
  209. if ( NULL == ghKernel32 )
  210. {
  211. return FALSE;
  212. }
  213. gpCreateWaitableTimerW = ( PCREATEWAITABLETIMERW ) GetProcAddress( ghKernel32, "CreateWaitableTimerW" );
  214. gpSetWaitableTimerW = ( PSETWAITABLETIMER ) GetProcAddress( ghKernel32, "SetWaitableTimer" );
  215. if ( ( NULL == gpCreateWaitableTimerW ) || ( NULL == gpSetWaitableTimerW ) )
  216. {
  217. FreeLibrary( ghKernel32 );
  218. ghKernel32 = NULL;
  219. return FALSE;
  220. }
  221. }
  222. if ( NULL == ghWaitableTimer )
  223. {
  224. ghWaitableTimer = gpCreateWaitableTimerW( NULL, TRUE, NULL );
  225. // We gotta big problem
  226. if ( NULL == ghWaitableTimer )
  227. {
  228. // Log an error here
  229. ERRORTRACE( ( LOG_WINMGMT, "Could not create a waitable timer for Resyncperf.\n" ) );
  230. }
  231. }
  232. if ( NULL == ghResyncThreadEvent )
  233. {
  234. ghResyncThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  235. // We gotta big problem
  236. if ( NULL == ghResyncThreadEvent )
  237. {
  238. // Log an event here
  239. ERRORTRACE( ( LOG_WINMGMT, "Could not create a ResyncThreadEvent event for Resyncperf.\n" ) );
  240. }
  241. }
  242. // This critical section won't be freed or deleted because of
  243. // potential timing issues. But since it's only one, I think
  244. // we can live with it.
  245. if ( NULL == g_pResyncCs )
  246. {
  247. g_pResyncCs = new CRITICAL_SECTION;
  248. // We gotta big problem
  249. if ( NULL == g_pResyncCs )
  250. {
  251. // Log an event here
  252. ERRORTRACE( ( LOG_WINMGMT, "Could not create a ResyncCs critical section for Resyncperf.\n" ) );
  253. }
  254. else
  255. {
  256. InitializeCriticalSection( g_pResyncCs );
  257. }
  258. }
  259. gfResyncInit = ( NULL != ghWaitableTimer &&
  260. NULL != g_pResyncCs &&
  261. NULL != ghResyncThreadEvent );
  262. // Read the initialization information
  263. CNTRegistry reg;
  264. if ( CNTRegistry::no_error == reg.Open( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\WBEM\\CIMOM" ) )
  265. {
  266. long lError = reg.GetDWORD( L"ADAPDelay", &gdwADAPDelaySec );
  267. if ( CNTRegistry::no_error == lError )
  268. {
  269. //This is what we want
  270. }
  271. else if ( CNTRegistry::not_found == lError )
  272. {
  273. // Not set, so add it
  274. reg.SetDWORD( L"ADAPDelay", WMIADAP_DEFAULT_DELAY );
  275. gdwADAPDelaySec = WMIADAP_DEFAULT_DELAY;
  276. }
  277. else
  278. {
  279. // Error
  280. ERRORTRACE( ( LOG_WINMGMT, "ResyncPerf experienced an error while attempting to read the WMIADAPDelay value in the CIMOM subkey. Continuing using a default value.\n" ) );
  281. gdwADAPDelaySec = WMIADAP_DEFAULT_DELAY;
  282. }
  283. }
  284. else
  285. {
  286. // Error
  287. ERRORTRACE( ( LOG_WINMGMT, "ResyncPerf could not open the CIMOM subkey to read initialization data. Continuing using a default value.\n" ) );
  288. gdwADAPDelaySec = WMIADAP_DEFAULT_DELAY;
  289. }
  290. return gfResyncInit;
  291. }
  292. // PLEASE NOTE - THIS FUNCTION IS NOT REENTRANT! PLEASE DO NOT CALL IT ON MULTIPLE THREADS!
  293. void ResyncPerf( HANDLE hTerminate )
  294. {
  295. // Make sure this is Win2000 or greater
  296. if ( !IsW2KOrMore() )
  297. {
  298. return;
  299. }
  300. // Assume that we should check the timer
  301. BOOL fFirstTime = !gfResyncInit;
  302. if ( !InitResync() )
  303. return;
  304. // Auto FreeLibrary for the gpKernel32 Library handle
  305. CAutoFreeLib aflKernel32;
  306. EnterCriticalSection( g_pResyncCs );
  307. // Now, if this or the first time, or the spawned resyncflag is set to TRUE, then we need
  308. // to kick off another thread. By checking gfSpawnedResync in a critical section, since
  309. // it only gets set in the same critical section, we ensure that we will resignal as needed
  310. // as well as only kick off a thread when we really need to.
  311. BOOL fSpawnThread = ( fFirstTime || gfSpawnedResync );
  312. if ( !fSpawnThread )
  313. {
  314. // We are here because we don't appear to have spawned a resync.
  315. // This is either because we are servicing many lodctr requests
  316. // within our time delay, or a dredger was started and
  317. // a previous request request to dredge is waiting for
  318. // the process to complete. If the child process handle
  319. // is not NULL, there is no real need to reset the
  320. // waitable timer
  321. if ( NULL == ghChildProcessHandle && ghResyncThreadHandle )
  322. {
  323. // Reset the timer here
  324. ResetResyncTimer( ghWaitableTimer );
  325. }
  326. }
  327. LeaveCriticalSection( g_pResyncCs );
  328. if ( fSpawnThread )
  329. {
  330. HANDLE ahHandle[2];
  331. if ( NULL != ghResyncThreadHandle )
  332. {
  333. ahHandle[0] = hTerminate;
  334. ahHandle[1] = ghResyncThreadHandle;
  335. // Wait for ten seconds on this handle. If it is not signalled, something is
  336. // direly wrong. We're probably not going to be able to kick off a dredge
  337. // so put some info to this effect in the error log. The only time we should
  338. // have contention here, is when a lodctr event is signalled, just as the timer
  339. // becomes signalled. The resync thread will wake up and start another dredge
  340. // this thread will wait for the other thread to complete before continuing.
  341. // We will kick off another resync thread, which will start another dredge,
  342. // but it will wait for the first dredge to continue. This is a worst case
  343. // scenario, and arguably kicking off two dredges isn't that bad of a bailout
  344. DWORD dwRet = WaitForMultipleObjects( 2, ahHandle, FALSE, 10000 );
  345. // We're done
  346. if ( dwRet == WAIT_OBJECT_0 )
  347. {
  348. return;
  349. }
  350. if ( dwRet != WAIT_OBJECT_0 + 1 )
  351. {
  352. ERRORTRACE( ( LOG_WINMGMT, "The wait for a termination event or ResyncThreadHandle timed out in Resyncperf.\n" ) );
  353. return;
  354. }
  355. CloseHandle( ghResyncThreadHandle );
  356. ghResyncThreadHandle = NULL;
  357. }
  358. EnterCriticalSection( g_pResyncCs );
  359. DWORD dwThreadId = 0;
  360. RESYNCPERFDATASTRUCT* pResyncData = new RESYNCPERFDATASTRUCT;
  361. // Boy are we low on memory!
  362. if ( NULL == pResyncData )
  363. {
  364. LeaveCriticalSection( g_pResyncCs );
  365. // Log an event here
  366. ERRORTRACE( ( LOG_WINMGMT, "Could not create a RESYNCPERFDATASTRUCT in Resyncperf.\n" ) );
  367. return;
  368. }
  369. // Store the data for the resync operation
  370. pResyncData->m_hTerminate = hTerminate;
  371. pResyncData->m_hWaitableTimer = ghWaitableTimer;
  372. pResyncData->m_pcs = g_pResyncCs;
  373. ghResyncThreadHandle = (HANDLE) _beginthreadex( NULL, 0, ResyncPerfThread, (void*) pResyncData,
  374. 0, (unsigned int *) &gdwResyncThreadId );
  375. LeaveCriticalSection( g_pResyncCs );
  376. if ( NULL == ghResyncThreadHandle )
  377. {
  378. LeaveCriticalSection( g_pResyncCs );
  379. // Log an event here
  380. ERRORTRACE( ( LOG_WINMGMT, "Could not create a ResyncPerfThread thread in Resyncperf.\n" ) );
  381. return;
  382. }
  383. else
  384. {
  385. // Wait for the resync thread event to be signalled by the thread we just started.
  386. // If it doesn't signal in 10 seconds, something is VERY wrong
  387. DWORD dwWait = WaitForSingleObject( ghResyncThreadEvent, INFINITE );
  388. if ( dwWait != WAIT_OBJECT_0 )
  389. {
  390. // Log an event
  391. ERRORTRACE( ( LOG_WINMGMT, "The ResyncPerfThread thread never signaled the ghResyncThreadEvent in Resyncperf.\n" ) );
  392. return;
  393. }
  394. }
  395. } // IF fSpawnThread
  396. }