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.

734 lines
21 KiB

  1. /**********************************************************************/
  2. /** Microsoft Passport **/
  3. /** Copyright(c) Microsoft Corporation, 1999 - 2001 **/
  4. /**********************************************************************/
  5. /*
  6. ppnotificationthread.cpp
  7. implement the methods runing a separte thread watching for registry
  8. changes, and timer for CCD refresh
  9. FILE HISTORY:
  10. */
  11. #include "precomp.h"
  12. PassportLockedInteger PpNotificationThread::m_NextHandle;
  13. //
  14. // Constructor
  15. //
  16. PpNotificationThread::PpNotificationThread()
  17. {
  18. LocalConfigurationUpdated();
  19. AddLocalConfigClient(dynamic_cast<IConfigurationUpdate*>(this), NULL);
  20. }
  21. //
  22. // Destructor
  23. //
  24. PpNotificationThread::~PpNotificationThread()
  25. {
  26. }
  27. //
  28. // Add a CCD client to the notification list.
  29. //
  30. HRESULT
  31. PpNotificationThread::AddCCDClient(
  32. tstring& strCCDName,
  33. ICCDUpdate* piUpdate,
  34. HANDLE* phClientHandle)
  35. {
  36. HRESULT hr;
  37. NOTIFICATION_CLIENT clientInfo;
  38. try
  39. {
  40. clientInfo.dwNotificationType = NOTIF_CCD;
  41. clientInfo.NotificationInterface.piCCDUpdate = piUpdate;
  42. clientInfo.strCCDName = strCCDName;
  43. clientInfo.hClientHandle = (HANDLE)(LONG_PTR)(++m_NextHandle);
  44. {
  45. PassportGuard<PassportLock> guard(m_ClientListLock);
  46. m_ClientList.push_back(clientInfo);
  47. }
  48. if(phClientHandle != NULL)
  49. {
  50. *phClientHandle = clientInfo.hClientHandle;
  51. }
  52. hr = S_OK;
  53. }
  54. catch(...)
  55. {
  56. hr = E_OUTOFMEMORY;
  57. }
  58. return hr;
  59. }
  60. //
  61. // Add a configuration client to the notification list
  62. //
  63. HRESULT
  64. PpNotificationThread::AddLocalConfigClient(
  65. IConfigurationUpdate* piUpdate,
  66. HANDLE* phClientHandle)
  67. {
  68. HRESULT hr;
  69. NOTIFICATION_CLIENT clientInfo;
  70. clientInfo.dwNotificationType = NOTIF_CONFIG;
  71. clientInfo.NotificationInterface.piConfigUpdate = piUpdate;
  72. clientInfo.hClientHandle = (HANDLE)(LONG_PTR)(++m_NextHandle);
  73. {
  74. PassportGuard<PassportLock> guard(m_ClientListLock);
  75. try
  76. {
  77. m_ClientList.push_back(clientInfo);
  78. }
  79. catch(...)
  80. {
  81. hr = E_OUTOFMEMORY;
  82. goto Ret;
  83. }
  84. }
  85. if(phClientHandle != NULL)
  86. {
  87. *phClientHandle = clientInfo.hClientHandle;
  88. }
  89. hr = S_OK;
  90. Ret:
  91. return hr;
  92. }
  93. //
  94. // Remove a client (either type) from the notification list.
  95. //
  96. HRESULT
  97. PpNotificationThread::RemoveClient(
  98. HANDLE hClientHandle)
  99. {
  100. HRESULT hr;
  101. PassportGuard<PassportLock> guard(m_ClientListLock);
  102. for(CLIENT_LIST::iterator it = m_ClientList.begin(); it != m_ClientList.end(); it++)
  103. {
  104. if((*it).hClientHandle == hClientHandle)
  105. {
  106. m_ClientList.erase(it);
  107. hr = S_OK;
  108. goto Cleanup;
  109. }
  110. }
  111. hr = E_FAIL;
  112. Cleanup:
  113. return hr;
  114. }
  115. //
  116. // Do a manual refresh of a CCD.
  117. //
  118. HRESULT
  119. PpNotificationThread::GetCCD(
  120. tstring& strCCDName,
  121. IXMLDocument** ppiXMLDocument,
  122. BOOL bForceFetch)
  123. {
  124. HRESULT hr;
  125. PpShadowDocument* pShadowDoc;
  126. CCD_INFO ccdInfo;
  127. {
  128. PassportGuard<PassportLock> guard(m_CCDInfoLock);
  129. // Get the CCD Information for the requested CCD
  130. if(!GetCCDInfo(strCCDName, ccdInfo))
  131. {
  132. hr = E_INVALIDARG;
  133. pShadowDoc = NULL;
  134. goto Cleanup;
  135. }
  136. // Create a new shadow document for the CCD
  137. if(ccdInfo.strCCDLocalFile.empty())
  138. pShadowDoc = new PpShadowDocument(ccdInfo.strCCDURL);
  139. else
  140. pShadowDoc = new PpShadowDocument(ccdInfo.strCCDURL, ccdInfo.strCCDLocalFile);
  141. }
  142. if(!pShadowDoc)
  143. {
  144. hr = E_OUTOFMEMORY;
  145. goto Cleanup;
  146. }
  147. // Do the update.
  148. hr = pShadowDoc->GetDocument(ppiXMLDocument, bForceFetch);
  149. //BUGBUG This is weird because currently other clients of this CCD will NOT get
  150. // notified. I don't want to loop through the notification list here for
  151. // two reasons:
  152. //
  153. // 1. The caller might be in the notification list and I don't to notify
  154. // unnecessarily.
  155. // 2. Don't want to put the overhead of notifying all clients on the
  156. // caller's thread.
  157. //
  158. // The ideal solution would be to wake up our dedicated thread and have it
  159. // do the notification. I haven't been able to find a way to signal a
  160. // waitable time though.
  161. Cleanup:
  162. if(pShadowDoc != NULL)
  163. delete pShadowDoc;
  164. return hr;
  165. }
  166. //
  167. //
  168. // register configure change notification
  169. // register CCD update timeer
  170. // call client notification sink
  171. //
  172. //
  173. void
  174. PpNotificationThread::run(void)
  175. {
  176. {
  177. HANDLE* pHandleArray = NULL;
  178. LONG lResult;
  179. PassportEvent RegChangeEvent(FALSE,FALSE);
  180. DWORD dwCurCCDInfo;
  181. DWORD dwCurArrayLen;
  182. DWORD dwWaitResult;
  183. DWORD dwError;
  184. CCD_INFO_LIST::iterator it;
  185. CRegKey PassportKey;
  186. BOOL bKeyOpened;
  187. HRESULT hr;
  188. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  189. _ASSERT(hr != S_FALSE);
  190. lResult = PassportKey.Open(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Passport"), KEY_NOTIFY);
  191. bKeyOpened = (lResult == ERROR_SUCCESS);
  192. m_StartupThread.Set();
  193. while(WaitForSingleObject(m_ShutdownThread, 0) != WAIT_OBJECT_0)
  194. {
  195. if(bKeyOpened)
  196. {
  197. lResult = RegNotifyChangeKeyValue((HKEY)PassportKey, TRUE,
  198. REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
  199. (HANDLE)RegChangeEvent,
  200. TRUE);
  201. if(lResult != ERROR_SUCCESS)
  202. dwError = GetLastError();
  203. }
  204. {
  205. PassportGuard<PassportLock> guard(m_CCDInfoLock);
  206. dwCurArrayLen = m_aciCCDInfoList.size() + 2;
  207. pHandleArray = new HANDLE[dwCurArrayLen];
  208. if(pHandleArray == NULL)
  209. {
  210. // BUGBUG Throw a low-memory alert here?
  211. continue;
  212. }
  213. pHandleArray[0] = (HANDLE)m_ShutdownThread; // Handle 0 always contains the thread shutdown signal.
  214. pHandleArray[1] = (HANDLE)RegChangeEvent; // Handle 1 always contains the registry change event.
  215. for(it = m_aciCCDInfoList.begin(), dwCurCCDInfo = 0; it != m_aciCCDInfoList.end(); it++, dwCurCCDInfo++)
  216. {
  217. pHandleArray[dwCurCCDInfo + 2] = (*it).hCCDTimer;
  218. }
  219. }
  220. dwWaitResult = WaitForMultipleObjects(dwCurArrayLen,
  221. pHandleArray,
  222. FALSE,
  223. INFINITE);
  224. switch(dwWaitResult)
  225. {
  226. case WAIT_FAILED:
  227. dwError = GetLastError();
  228. break;
  229. // Thread shutdown has been signalled. Exit this thread.
  230. case WAIT_OBJECT_0:
  231. goto Cleanup;
  232. // Registry change has been signalled. Notify all local config clients.
  233. case WAIT_OBJECT_0 + 1:
  234. {
  235. PassportGuard<PassportLock> guard(m_ClientListLock);
  236. CLIENT_LIST::iterator cl_iter;
  237. for(cl_iter = m_ClientList.begin(); cl_iter != m_ClientList.end(); cl_iter++)
  238. {
  239. if((*cl_iter).dwNotificationType == NOTIF_CONFIG)
  240. {
  241. (*cl_iter).NotificationInterface.piConfigUpdate->LocalConfigurationUpdated();
  242. }
  243. }
  244. }
  245. break;
  246. // One of the CCD timers has been signalled. Read the CCD and notify all CCD clients.
  247. default:
  248. {
  249. IXMLDocumentPtr xmlDoc;
  250. PpShadowDocument ShadowDoc;
  251. DWORD dwInfoIndex = dwWaitResult - WAIT_OBJECT_0 - 2;
  252. //
  253. // Due to the ugly nature of the code an allocation can fail within a constructor
  254. // and cause AVs in this code. So unfortunately we'll wrap this code to
  255. // account for that.
  256. //
  257. try
  258. {
  259. m_CCDInfoLock.acquire();
  260. CCD_INFO_LIST aciTempCCDInfoList(m_aciCCDInfoList);
  261. m_CCDInfoLock.release();
  262. m_aciCCDInfoList[dwInfoIndex].SetTimer();
  263. // Fetch the CCD
  264. ShadowDoc.SetURL(aciTempCCDInfoList[dwInfoIndex].strCCDURL);
  265. if(!aciTempCCDInfoList[dwInfoIndex].strCCDLocalFile.empty())
  266. ShadowDoc.SetLocalFile(aciTempCCDInfoList[dwInfoIndex].strCCDLocalFile);
  267. if(ShadowDoc.GetDocument(&xmlDoc) == S_OK)
  268. {
  269. PassportGuard<PassportLock> guard(m_ClientListLock);
  270. LPCTSTR pszUpdatedName = aciTempCCDInfoList[dwInfoIndex].strCCDName.c_str();
  271. // Loop through client list and call any clients registered for the CCD that
  272. // changed.
  273. CLIENT_LIST::iterator cl_iter;
  274. for(cl_iter = m_ClientList.begin(); cl_iter != m_ClientList.end(); cl_iter++)
  275. {
  276. if(lstrcmpi(pszUpdatedName, (*cl_iter).strCCDName.c_str()) == 0)
  277. {
  278. (*cl_iter).NotificationInterface.piCCDUpdate->CCDUpdated(
  279. pszUpdatedName,
  280. (IXMLDocument*)xmlDoc);
  281. }
  282. }
  283. }
  284. }
  285. catch(...)
  286. {
  287. if (g_pAlert)
  288. {
  289. g_pAlert->report(PassportAlertInterface::ERROR_TYPE, PM_CCD_NOT_LOADED, 0);
  290. }
  291. }
  292. }
  293. break;
  294. }
  295. delete [] pHandleArray;
  296. pHandleArray = NULL;
  297. }
  298. Cleanup:
  299. if(pHandleArray != NULL)
  300. delete [] pHandleArray;
  301. CoUninitialize();
  302. }
  303. m_ShutdownAck.Set();
  304. }
  305. //
  306. // Update our configuration. This is called from the constructor, and
  307. // from the notification thread whenever the registry changes.
  308. //
  309. void
  310. PpNotificationThread::LocalConfigurationUpdated()
  311. {
  312. CRegKey NexusRegKey;
  313. LONG lResult;
  314. DWORD dwIndex;
  315. DWORD dwNameLen;
  316. DWORD dwDefaultRefreshInterval;
  317. TCHAR achNameBuf[64];
  318. lResult = NexusRegKey.Open(HKEY_LOCAL_MACHINE,
  319. TEXT("Software\\Microsoft\\Passport\\Nexus"),
  320. KEY_READ);
  321. if(lResult != ERROR_SUCCESS)
  322. {
  323. //BUGBUG This is a required reg key, throw an event.
  324. return;
  325. }
  326. // Get the default refresh interval.
  327. lResult = NexusRegKey.QueryDWORDValue(TEXT("CCDRefreshInterval"), dwDefaultRefreshInterval);
  328. if(lResult != ERROR_SUCCESS)
  329. {
  330. //BUGBUG This is a required reg value, throw an event.
  331. return;
  332. }
  333. //
  334. // Lock down the list.
  335. //
  336. {
  337. PassportGuard<PassportLock> guard(m_CCDInfoLock);
  338. //
  339. // Loop through existing list and remove any items whose corresponding keys
  340. // have been removed.
  341. //
  342. CCD_INFO_LIST::iterator it;
  343. for(it = m_aciCCDInfoList.begin(); it != m_aciCCDInfoList.end(); )
  344. {
  345. CRegKey CCDRegKey;
  346. lResult = CCDRegKey.Open((HKEY)NexusRegKey, (*it).strCCDName.c_str(), KEY_READ);
  347. if(lResult != ERROR_SUCCESS)
  348. {
  349. it = m_aciCCDInfoList.erase(it);
  350. }
  351. else
  352. it++;
  353. }
  354. //
  355. // Loop through each subkey and add/update the CCD info therein.
  356. //
  357. dwIndex = 0;
  358. dwNameLen = sizeof(achNameBuf) / sizeof(achNameBuf[0]);
  359. while(RegEnumKeyEx((HKEY)NexusRegKey, dwIndex,achNameBuf, &dwNameLen, NULL, NULL, NULL, NULL ) == ERROR_SUCCESS)
  360. {
  361. CRegKey CCDRegKey;
  362. lResult = CCDRegKey.Open((HKEY)NexusRegKey, achNameBuf, KEY_READ);
  363. if(lResult == ERROR_SUCCESS)
  364. {
  365. ReadCCDInfo(tstring(achNameBuf), dwDefaultRefreshInterval, CCDRegKey);
  366. }
  367. dwIndex++;
  368. dwNameLen = sizeof(achNameBuf);
  369. }
  370. }
  371. }
  372. //
  373. // This method starts the thread and then wait for the thread to get going.
  374. //
  375. bool
  376. PpNotificationThread::start(void)
  377. {
  378. m_StartupThread.Reset();
  379. bool bReturn = PassportThread::start();
  380. //
  381. // Now wait for the thread to start.
  382. //
  383. WaitForSingleObject((HANDLE)m_StartupThread, INFINITE);
  384. return bReturn;
  385. }
  386. //
  387. // This method just signals the shutdown event causing the thread to terminate immediately.
  388. //
  389. void
  390. PpNotificationThread::stop(void)
  391. {
  392. m_ShutdownThread.Set();
  393. WaitForSingleObject(m_ShutdownAck, 1000);
  394. // give it a chance to terminate
  395. Sleep(20);
  396. }
  397. //
  398. // Private method for reading the CCD info for a single CCD subkey from
  399. // the registry.
  400. //
  401. BOOL
  402. PpNotificationThread::ReadCCDInfo(
  403. tstring& strCCDName,
  404. DWORD dwDefaultRefreshInterval,
  405. CRegKey& CCDRegKey
  406. )
  407. {
  408. BOOL bReturn = TRUE;
  409. LONG lResult;
  410. DWORD dwBufLen = 0;
  411. DWORD dwType;
  412. CCD_INFO_LIST::iterator it;
  413. LPTSTR pszRemoteFile = NULL;
  414. LPTSTR pszLocalFile = TEXT("");
  415. BOOL fLocalFileAllocated = FALSE;
  416. DWORD dwCCDRefreshInterval;
  417. LPTSTR pszTempFile = NULL;
  418. //
  419. // Read in the remote path to the CCD.
  420. // CCDRemoteFile is the only required value. If it's not there, return FALSE.
  421. //
  422. lResult = CCDRegKey.QueryStringValue(TEXT("CCDRemoteFile"), NULL, &dwBufLen);
  423. if(lResult == ERROR_SUCCESS)
  424. {
  425. pszRemoteFile = (LPTSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, dwBufLen * sizeof(TCHAR));
  426. if (NULL == pszRemoteFile)
  427. {
  428. bReturn = FALSE;
  429. goto Cleanup;
  430. }
  431. CCDRegKey.QueryStringValue(TEXT("CCDRemoteFile"), pszRemoteFile, &dwBufLen);
  432. while(*pszRemoteFile && _istspace(*pszRemoteFile))
  433. pszRemoteFile++;
  434. }
  435. else
  436. {
  437. bReturn = FALSE;
  438. goto Cleanup;
  439. }
  440. //
  441. // Read in the refresh interval for this CCD.
  442. //
  443. lResult = CCDRegKey.QueryDWORDValue(TEXT("CCDRefreshInterval"), dwCCDRefreshInterval);
  444. if(lResult != ERROR_SUCCESS)
  445. dwCCDRefreshInterval = 0xFFFFFFFF;
  446. //
  447. // Read in local (backup) path for the CCD. This is an optional value. Use
  448. // empty string (initialized above) as the default.
  449. //
  450. lResult = CCDRegKey.QueryValue(TEXT("CCDLocalFile"), &dwType, NULL, &dwBufLen);
  451. if(lResult == ERROR_SUCCESS)
  452. {
  453. if (dwType == REG_EXPAND_SZ)
  454. {
  455. pszTempFile = (LPTSTR) LocalAlloc(LMEM_FIXED, dwBufLen);
  456. if (pszTempFile)
  457. {
  458. lResult = CCDRegKey.QueryValue(TEXT("CCDLocalFile"), &dwType, pszTempFile, &dwBufLen);
  459. if (lResult == ERROR_SUCCESS)
  460. {
  461. //
  462. // Expand out the environment variable
  463. //
  464. TCHAR tszTemp;
  465. dwBufLen = ExpandEnvironmentStrings(pszTempFile, &tszTemp, 1);
  466. if (dwBufLen > 1)
  467. {
  468. DWORD dwChars;
  469. pszLocalFile = (LPTSTR)LocalAlloc(LMEM_FIXED, dwBufLen * sizeof(TCHAR));
  470. if (NULL == pszLocalFile)
  471. {
  472. bReturn = FALSE;
  473. goto Cleanup;
  474. }
  475. else
  476. {
  477. fLocalFileAllocated = TRUE;
  478. }
  479. dwChars = ExpandEnvironmentStrings(pszTempFile, pszLocalFile, dwBufLen);
  480. if (dwChars > dwBufLen)
  481. {
  482. LocalFree(pszLocalFile);
  483. fLocalFileAllocated = FALSE;
  484. pszLocalFile = TEXT("");
  485. }
  486. while(*pszLocalFile && _istspace(*pszLocalFile)) pszLocalFile++;
  487. }
  488. }
  489. }
  490. }
  491. else if (dwType == REG_SZ)
  492. {
  493. pszLocalFile = (LPTSTR)LocalAlloc(LMEM_FIXED, dwBufLen * sizeof(TCHAR));
  494. if (NULL == pszLocalFile)
  495. {
  496. bReturn = FALSE;
  497. goto Cleanup;
  498. }
  499. else
  500. {
  501. fLocalFileAllocated = TRUE;
  502. }
  503. if (CCDRegKey.QueryValue(TEXT("CCDLocalFile"), &dwType, pszLocalFile, &dwBufLen) != ERROR_SUCCESS)
  504. {
  505. LocalFree(pszLocalFile);
  506. fLocalFileAllocated = FALSE;
  507. pszLocalFile = TEXT("");
  508. }
  509. while(*pszLocalFile && _istspace(*pszLocalFile)) pszLocalFile++;
  510. }
  511. }
  512. //
  513. // If this CCD is already in the list, then update it.
  514. //
  515. for(it = m_aciCCDInfoList.begin(); it != m_aciCCDInfoList.end(); it++)
  516. {
  517. if(lstrcmp((*it).strCCDName.c_str(), strCCDName.c_str()) == 0)
  518. {
  519. // Check to see if the information has changed.
  520. if(lstrcmpi(pszRemoteFile, (*it).strCCDURL.c_str()) != 0 ||
  521. lstrcmpi(pszLocalFile, (*it).strCCDLocalFile.c_str()) != 0 ||
  522. dwCCDRefreshInterval != (*it).dwCCDRefreshInterval ||
  523. dwDefaultRefreshInterval != (*it).dwDefaultRefreshInterval
  524. )
  525. {
  526. DWORD dwOldRefreshInterval = ((*it).dwCCDRefreshInterval == 0xFFFFFFFF ?
  527. (*it).dwDefaultRefreshInterval :
  528. (*it).dwCCDRefreshInterval);
  529. DWORD dwNewRefreshInterval = (dwCCDRefreshInterval == 0xFFFFFFFF ?
  530. dwDefaultRefreshInterval :
  531. dwCCDRefreshInterval);
  532. (*it).strCCDURL = pszRemoteFile;
  533. (*it).strCCDLocalFile = pszLocalFile;
  534. (*it).dwCCDRefreshInterval = dwCCDRefreshInterval;
  535. (*it).dwDefaultRefreshInterval = dwDefaultRefreshInterval;
  536. if(dwOldRefreshInterval != dwNewRefreshInterval)
  537. (*it).SetTimer();
  538. }
  539. break;
  540. }
  541. }
  542. //
  543. // This is a new CCD, add it to the list.
  544. //
  545. if(it == m_aciCCDInfoList.end())
  546. {
  547. CCD_INFO ccdInfo;
  548. ccdInfo.strCCDName = strCCDName;
  549. ccdInfo.strCCDURL = pszRemoteFile;
  550. ccdInfo.strCCDLocalFile = pszLocalFile;
  551. ccdInfo.dwCCDRefreshInterval = dwCCDRefreshInterval;
  552. ccdInfo.dwDefaultRefreshInterval = dwDefaultRefreshInterval;
  553. ccdInfo.SetTimer();
  554. try
  555. {
  556. m_aciCCDInfoList.push_back(ccdInfo);
  557. }
  558. catch(...)
  559. {
  560. bReturn = FALSE;
  561. goto Cleanup;
  562. }
  563. }
  564. bReturn = TRUE;
  565. Cleanup:
  566. if (pszTempFile)
  567. {
  568. LocalFree(pszTempFile);
  569. }
  570. if (pszRemoteFile)
  571. {
  572. LocalFree(pszRemoteFile);
  573. }
  574. if (fLocalFileAllocated && pszLocalFile)
  575. {
  576. LocalFree(pszLocalFile);
  577. }
  578. return bReturn;
  579. }
  580. //
  581. // Private method for retrieving a CCD_INFO structure given the CCD name.
  582. //
  583. BOOL
  584. PpNotificationThread::GetCCDInfo(
  585. tstring& strCCDName,
  586. CCD_INFO& ccdInfo
  587. )
  588. {
  589. CCD_INFO_LIST::iterator it;
  590. for(it = m_aciCCDInfoList.begin(); it != m_aciCCDInfoList.end(); it++)
  591. {
  592. if(lstrcmpi((*it).strCCDName.c_str(), strCCDName.c_str()) == 0)
  593. {
  594. ccdInfo = (*it);
  595. return TRUE;
  596. }
  597. }
  598. return FALSE;
  599. }