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.

842 lines
16 KiB

  1. /*++
  2. Copyright (c) 2000-2002 Microsoft Corporation
  3. Module Name:
  4. notify.c
  5. Abstract:
  6. DNS Resolver Service.
  7. Notification thread
  8. - host file changes
  9. - registry config changes
  10. Author:
  11. Jim Gilroy (jamesg) November 2000
  12. Revision History:
  13. jamesg Nov 2001 -- IP6
  14. --*/
  15. #include "local.h"
  16. //
  17. // DHCP refresh call
  18. //
  19. extern
  20. DWORD
  21. DhcpStaticRefreshParams(
  22. IN LPWSTR Adapter
  23. );
  24. //
  25. // Host file directory
  26. //
  27. #define HOSTS_FILE_DIRECTORY L"\\drivers\\etc"
  28. //
  29. // Notify globals
  30. //
  31. DWORD g_NotifyThreadId = 0;
  32. HANDLE g_hNotifyThread = NULL;
  33. HANDLE g_hHostFileChange = NULL;
  34. HANDLE g_hRegistryChange = NULL;
  35. HKEY g_hCacheKey = NULL;
  36. PSTR g_pmszAlternateNames = NULL;
  37. //
  38. // Private protos
  39. //
  40. VOID
  41. CleanupRegistryMonitoring(
  42. VOID
  43. );
  44. HANDLE
  45. CreateHostsFileChangeHandle(
  46. VOID
  47. )
  48. /*++
  49. Routine Description:
  50. Create hosts file change handle.
  51. Arguments:
  52. None.
  53. Return Value:
  54. None.
  55. --*/
  56. {
  57. HANDLE changeHandle;
  58. PWSTR psystemDirectory = NULL;
  59. UINT len;
  60. WCHAR hostDirectory[ MAX_PATH*2 ];
  61. DNSDBG( INIT, ( "CreateHostsFileChangeHandle\n" ));
  62. //
  63. // build host file name
  64. //
  65. len = GetSystemDirectory( hostDirectory, MAX_PATH );
  66. if ( !len || len>MAX_PATH )
  67. {
  68. DNSLOG_F1( "Error: Failed to get system directory" );
  69. DNSLOG_F1( "NotifyThread exiting." );
  70. return( NULL );
  71. }
  72. wcscat( hostDirectory, HOSTS_FILE_DIRECTORY );
  73. //
  74. // drop change notify on host file directory
  75. //
  76. changeHandle = FindFirstChangeNotification(
  77. hostDirectory,
  78. FALSE,
  79. FILE_NOTIFY_CHANGE_FILE_NAME |
  80. FILE_NOTIFY_CHANGE_LAST_WRITE );
  81. if ( changeHandle == INVALID_HANDLE_VALUE )
  82. {
  83. DNSLOG_F1( "NotifyThread failed to get handle from" );
  84. DNSLOG_F2(
  85. "Failed to get hosts file change handle.\n"
  86. "Error code: <0x%.8X>",
  87. GetLastError() );
  88. return( NULL );
  89. }
  90. return( changeHandle );
  91. }
  92. //
  93. // Registry change monitoring
  94. //
  95. DNS_STATUS
  96. InitializeRegistryMonitoring(
  97. VOID
  98. )
  99. /*++
  100. Routine Description:
  101. Setup registry change monitoring.
  102. Arguments:
  103. None
  104. Globals:
  105. g_pmszAlternateNames -- set with current alternate names value
  106. g_hCacheKey -- cache reg key is opened
  107. g_hRegistryChange -- creates event to be signalled on change notify
  108. Return Value:
  109. ERROR_SUCCESS if successful.
  110. Error code on failure.
  111. --*/
  112. {
  113. DNS_STATUS status;
  114. DNSDBG( TRACE, (
  115. "InitializeRegistryMonitoring()\n" ));
  116. //
  117. // open monitoring regkey at DnsCache\Parameters
  118. // set on parameters key which always exists rather than
  119. // explicitly on alternate names key, which may not
  120. //
  121. status = RegOpenKeyExW(
  122. HKEY_LOCAL_MACHINE,
  123. DNS_CACHE_KEY,
  124. 0,
  125. KEY_READ,
  126. & g_hCacheKey );
  127. if ( status != NO_ERROR )
  128. {
  129. goto Failed;
  130. }
  131. g_hRegistryChange = CreateEvent(
  132. NULL, // no security
  133. FALSE, // auto-reset
  134. FALSE, // start non-signalled
  135. NULL // no name
  136. );
  137. if ( !g_hRegistryChange )
  138. {
  139. status = GetLastError();
  140. goto Failed;
  141. }
  142. //
  143. // set change notify
  144. //
  145. status = RegNotifyChangeKeyValue(
  146. g_hCacheKey,
  147. TRUE, // watch subtree
  148. REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
  149. g_hRegistryChange,
  150. TRUE // async, func doesn't block
  151. );
  152. if ( status != NO_ERROR )
  153. {
  154. goto Failed;
  155. }
  156. //
  157. // read alternate computer names
  158. // - need value to compare when we get a hit on change-notify
  159. // - read can fail -- value stays NULL
  160. //
  161. Reg_GetValue(
  162. NULL, // no session
  163. g_hCacheKey, // cache key
  164. RegIdAlternateNames,
  165. REGTYPE_ALTERNATE_NAMES,
  166. & g_pmszAlternateNames
  167. );
  168. goto Done;
  169. Failed:
  170. //
  171. // cleanup
  172. //
  173. CleanupRegistryMonitoring();
  174. Done:
  175. DNSDBG( TRACE, (
  176. "Leave InitializeRegistryMonitoring() => %d\n"
  177. "\tpAlternateNames = %p\n"
  178. "\thChangeEvent = %p\n"
  179. "\thCacheKey = %p\n",
  180. status,
  181. g_pmszAlternateNames,
  182. g_hRegistryChange,
  183. g_hCacheKey
  184. ));
  185. return status;
  186. }
  187. DNS_STATUS
  188. RestartRegistryMonitoring(
  189. VOID
  190. )
  191. /*++
  192. Routine Description:
  193. Check for change in alternate names.
  194. Arguments:
  195. None
  196. Globals:
  197. g_pmszAlternateNames -- read
  198. g_hCacheKey -- used for read
  199. g_hRegistryChange -- used to restart change-notify
  200. Return Value:
  201. TRUE if alternate names has changed.
  202. FALSE otherwise.
  203. --*/
  204. {
  205. DNS_STATUS status;
  206. DNSDBG( TRACE, (
  207. "RestartRegistryMonitoring()\n" ));
  208. //
  209. // sanity check
  210. //
  211. if ( !g_hCacheKey || !g_hRegistryChange )
  212. {
  213. ASSERT( g_hCacheKey && g_hRegistryChange );
  214. return ERROR_INVALID_PARAMETER;
  215. }
  216. //
  217. // restart change notify
  218. //
  219. status = RegNotifyChangeKeyValue(
  220. g_hCacheKey,
  221. TRUE, // watch subtree
  222. REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
  223. g_hRegistryChange,
  224. TRUE // async, func doesn't block
  225. );
  226. if ( status != NO_ERROR )
  227. {
  228. DNSDBG( ANY, (
  229. "RegChangeNotify failed! %d\n",
  230. status ));
  231. ASSERT( FALSE );
  232. }
  233. return status;
  234. }
  235. VOID
  236. CleanupRegistryMonitoring(
  237. VOID
  238. )
  239. /*++
  240. Routine Description:
  241. Cleanup registry monitoring.
  242. Arguments:
  243. None
  244. Globals:
  245. g_pmszAlternateNames -- is freed
  246. g_hCacheKey -- cache reg key is closed
  247. g_hRegistryChange -- is closed
  248. Return Value:
  249. None
  250. --*/
  251. {
  252. DNS_STATUS status;
  253. DNSDBG( TRACE, (
  254. "CleanupRegistryMonitoring()\n" ));
  255. if ( g_hHostFileChange )
  256. {
  257. CloseHandle( g_hHostFileChange );
  258. g_hHostFileChange = NULL;
  259. }
  260. // cleanup registry change stuff
  261. DnsApiFree( g_pmszAlternateNames );
  262. g_pmszAlternateNames = NULL;
  263. RegCloseKey( g_hCacheKey );
  264. g_hCacheKey = NULL;
  265. }
  266. BOOL
  267. CheckForAlternateNamesChange(
  268. VOID
  269. )
  270. /*++
  271. Routine Description:
  272. Check for change in alternate names.
  273. Arguments:
  274. None
  275. Globals:
  276. g_pmszAlternateNames -- read
  277. g_hCacheKey -- used for read
  278. Return Value:
  279. TRUE if alternate names has changed.
  280. FALSE otherwise.
  281. --*/
  282. {
  283. DNS_STATUS status;
  284. BOOL fcheck = TRUE;
  285. PCHAR palternateNames = NULL;
  286. DNSDBG( TRACE, (
  287. "CheckForAlternateNamesChange()\n" ));
  288. //
  289. // sanity check
  290. //
  291. if ( !g_hCacheKey || !g_hRegistryChange )
  292. {
  293. ASSERT( g_hCacheKey && g_hRegistryChange );
  294. return FALSE;
  295. }
  296. //
  297. // read alternate computer names
  298. // - need value to compare when we get a hit on change-notify
  299. // - read can fail -- value stays NULL
  300. //
  301. Reg_GetValue(
  302. NULL, // no session
  303. g_hCacheKey, // cache key
  304. RegIdAlternateNames,
  305. REGTYPE_ALTERNATE_NAMES,
  306. & palternateNames
  307. );
  308. //
  309. // detect alternate names change
  310. //
  311. if ( palternateNames || g_pmszAlternateNames )
  312. {
  313. if ( !palternateNames || !g_pmszAlternateNames )
  314. {
  315. goto Cleanup;
  316. }
  317. if ( !MultiSz_Equal_A(
  318. palternateNames,
  319. g_pmszAlternateNames ) )
  320. {
  321. goto Cleanup;
  322. }
  323. }
  324. fcheck = FALSE;
  325. Cleanup:
  326. DnsApiFree( palternateNames );
  327. DNSDBG( TRACE, (
  328. "Leave CheckForAlternateNamesChange() => %d\n",
  329. fcheck ));
  330. return fcheck;
  331. }
  332. //
  333. // Notify thread routines
  334. //
  335. VOID
  336. ThreadShutdownWait(
  337. IN HANDLE hThread
  338. )
  339. /*++
  340. Routine Description:
  341. Wait on thread shutdown.
  342. Arguments:
  343. hThread -- thread handle that is shutting down
  344. Return Value:
  345. None.
  346. --*/
  347. {
  348. DWORD waitResult;
  349. if ( !hThread )
  350. {
  351. return;
  352. }
  353. DNSDBG( ANY, (
  354. "Waiting on shutdown of thread %d (%p)\n",
  355. hThread, hThread ));
  356. waitResult = WaitForSingleObject(
  357. hThread,
  358. 10000 );
  359. switch( waitResult )
  360. {
  361. case WAIT_OBJECT_0:
  362. break;
  363. default:
  364. // thread didn't stop -- need to kill it
  365. ASSERT( waitResult == WAIT_TIMEOUT );
  366. DNSLOG_F2( "Shutdown: thread %d not stopped, terminating", hThread );
  367. TerminateThread( hThread, 1 );
  368. break;
  369. }
  370. // close thread handle
  371. CloseHandle( hThread );
  372. }
  373. VOID
  374. NotifyThread(
  375. VOID
  376. )
  377. /*++
  378. Routine Description:
  379. Main notify thread.
  380. Arguments:
  381. None.
  382. Globals:
  383. g_hStopEvent -- waits on shutdown even
  384. Return Value:
  385. None.
  386. --*/
  387. {
  388. DWORD handleCount;
  389. DWORD waitResult;
  390. HANDLE handleArray[3];
  391. DNSDBG( INIT, (
  392. "\nStart NotifyThread\n" ));
  393. //
  394. // get file change handle
  395. //
  396. g_hHostFileChange = CreateHostsFileChangeHandle();
  397. //
  398. // init registry change-notify
  399. //
  400. InitializeRegistryMonitoring();
  401. //
  402. // wait on
  403. // - host file change => flush+rebuild cache
  404. // - registry change => reread config info
  405. // - shutdown => exit
  406. //
  407. handleArray[0] = g_hStopEvent;
  408. handleCount = 1;
  409. if ( g_hHostFileChange )
  410. {
  411. handleArray[handleCount++] = g_hHostFileChange;
  412. }
  413. if ( g_hRegistryChange )
  414. {
  415. handleArray[handleCount++] = g_hRegistryChange;
  416. }
  417. if ( handleCount == 1 )
  418. {
  419. DNSDBG( ANY, (
  420. "No change handles -- exit notify thread.\n" ));
  421. goto ThreadExit;
  422. }
  423. //
  424. // DCR: notify init failure handling
  425. // right now this loop is toast if eventing fails during any cycle
  426. //
  427. // should handle notify init failures
  428. // - have check-n-reinit (for each notification) in the loop
  429. // - when one fails, loop goes to timed wait (10m) and
  430. // then retries init in next cycle; timeout just cycles
  431. // loop
  432. //
  433. while( 1 )
  434. {
  435. waitResult = WaitForMultipleObjects(
  436. handleCount,
  437. handleArray,
  438. FALSE,
  439. INFINITE );
  440. switch( waitResult )
  441. {
  442. case WAIT_OBJECT_0:
  443. // shutdown event
  444. // - if stopping exit
  445. // - do garbage collection if required
  446. // - otherwise short wait to avoid spin if screwup
  447. // and not get thrashed by failed garbage collection
  448. DNSLOG_F1( "NotifyThread: Shutdown Event" );
  449. if ( g_StopFlag )
  450. {
  451. goto ThreadExit;
  452. }
  453. else if ( g_GarbageCollectFlag )
  454. {
  455. Cache_GarbageCollect( 0 );
  456. }
  457. ELSE_ASSERT_FALSE;
  458. Sleep( 1000 );
  459. if ( g_StopFlag )
  460. {
  461. goto ThreadExit;
  462. }
  463. continue;
  464. case WAIT_OBJECT_0 + 1:
  465. // host file change -- flush cache
  466. DNSLOG_F1( "NotifyThread: Host file change event" );
  467. // reset notification -- BEFORE reload
  468. if ( !FindNextChangeNotification( g_hHostFileChange ) )
  469. {
  470. DNSLOG_F1( "NotifyThread failed to get handle" );
  471. DNSLOG_F1( "from FindNextChangeNotification." );
  472. DNSLOG_F2( "Error code: <0x%.8X>", GetLastError() );
  473. goto ThreadExit;
  474. }
  475. Cache_Flush();
  476. break;
  477. case WAIT_OBJECT_0 + 2:
  478. // registry change notification -- flush cache and reload
  479. DNSLOG_F1( "NotifyThread: Registry change event" );
  480. // restart notification -- BEFORE reload
  481. RestartRegistryMonitoring();
  482. // rebuild config
  483. DNSDBG( ANY, ( "\nRegistry notification, rebuilding config.\n" ));
  484. HandleConfigChange(
  485. "Registry-notification",
  486. FALSE // no cache flush required
  487. );
  488. // check if alternate name change (to notify registrations)
  489. //
  490. // DCR: should notify on ordinary name change
  491. // - save old netinfo, get new, compare
  492. // - could wrap this into HandleConfigChange
  493. if ( CheckForAlternateNamesChange() )
  494. {
  495. DNSDBG( ANY, ( "\nAlternate name change, notify for reregistration!\n" ));
  496. DhcpStaticRefreshParams(
  497. NULL // global refresh, no particular adapter
  498. );
  499. }
  500. break;
  501. default:
  502. ASSERT( g_StopFlag );
  503. if ( g_StopFlag )
  504. {
  505. goto ThreadExit;
  506. }
  507. Sleep( 5000 );
  508. continue;
  509. }
  510. }
  511. ThreadExit:
  512. DNSDBG( INIT, (
  513. "NotifyThread exit\n" ));
  514. DNSLOG_F1( "NotifyThread exiting." );
  515. }
  516. VOID
  517. StartNotify(
  518. VOID
  519. )
  520. /*++
  521. Routine Description:
  522. Start notify thread.
  523. Arguments:
  524. None.
  525. Return Value:
  526. ERROR_SUCCESS if successful.
  527. ErrorCode on failure.
  528. --*/
  529. {
  530. //
  531. // clear
  532. //
  533. g_NotifyThreadId = 0;
  534. g_hNotifyThread = NULL;
  535. g_hHostFileChange = NULL;
  536. g_hRegistryChange = NULL;
  537. //
  538. // host file write monitor thread
  539. // keeps cache in sync when write made to host file
  540. //
  541. g_hNotifyThread = CreateThread(
  542. NULL,
  543. 0,
  544. (LPTHREAD_START_ROUTINE) NotifyThread,
  545. NULL,
  546. 0,
  547. &g_NotifyThreadId );
  548. if ( !g_hNotifyThread )
  549. {
  550. DNS_STATUS status = GetLastError();
  551. DNSLOG_F1( "ERROR: InitializeCache function failed to create" );
  552. DNSLOG_F1( " HOSTS file monitor thread." );
  553. DNSLOG_F2( " Error code: <0x%.8X>", status );
  554. DNSLOG_F1( " NOTE: Resolver service will continue to run." );
  555. DNSDBG( ANY, (
  556. "FAILED Notify thread start!\n"
  557. "\tstatus = %d\n",
  558. status ));
  559. }
  560. }
  561. VOID
  562. ShutdownNotify(
  563. VOID
  564. )
  565. /*++
  566. Routine Description:
  567. Shutdown notify thread.
  568. Arguments:
  569. None.
  570. Return Value:
  571. ERROR_SUCCESS if successful.
  572. ErrorCode on failure.
  573. --*/
  574. {
  575. DWORD waitResult;
  576. DNSDBG( INIT, ( "NotifyShutdown()\n" ));
  577. //
  578. // wait for notify thread to stop
  579. //
  580. ThreadShutdownWait( g_hNotifyThread );
  581. g_hNotifyThread = NULL;
  582. //
  583. // close notification handles
  584. //
  585. if ( g_hRegistryChange )
  586. {
  587. CloseHandle( g_hRegistryChange );
  588. g_hRegistryChange = NULL;
  589. }
  590. // registry monitoring cleanup
  591. CleanupRegistryMonitoring();
  592. // clear globals
  593. g_NotifyThreadId = 0;
  594. g_hNotifyThread = NULL;
  595. }
  596. //
  597. // End notify.c
  598. //