Source code of Windows XP (NT5)
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.

627 lines
14 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. dmnotify.c
  5. Abstract:
  6. Contains notification support for the Configuration Database Manager
  7. Each call to DmNotifyChangeKey adds a leaf to the notification tree. This
  8. tree is expected to be sparse, so each node is implemented as a linked list of
  9. subnodes and a linked list of leaves.
  10. When a registry modification occurs, the tree is traversed from the root
  11. to the leaf representing the key. Any leaves along the path are candidates
  12. for reporting a notification event.
  13. Author:
  14. John Vert (jvert) 9/18/1996
  15. Revision History:
  16. --*/
  17. #include "dmp.h"
  18. typedef struct _DM_NOTIFY_BRANCH {
  19. LIST_ENTRY SiblingList; // Links onto parent's ChildList.
  20. LIST_ENTRY ChildList; // Links onto child's SiblingList.
  21. LIST_ENTRY LeafList; // Links
  22. struct _DM_NOTIFY_BRANCH *Parent; // Parent
  23. USHORT NameLength;
  24. WCHAR KeyName[0]; // Name component (a single keyname, not a path)
  25. } DM_NOTIFY_BRANCH, *PDM_NOTIFY_BRANCH;
  26. typedef struct _DM_NOTIFY_LEAF {
  27. LIST_ENTRY SiblingList; // Links onto parent branch's ChildList
  28. LIST_ENTRY KeyList; // Links onto DMKEY.NotifyList
  29. LIST_ENTRY RundownList; // Passed into DmNotifyChangeKey, used for rundown
  30. HDMKEY hKey;
  31. DWORD CompletionFilter;
  32. DM_NOTIFY_CALLBACK NotifyCallback;
  33. DWORD_PTR Context1;
  34. DWORD_PTR Context2;
  35. PDM_NOTIFY_BRANCH Parent;
  36. BOOL WatchTree;
  37. } DM_NOTIFY_LEAF, *PDM_NOTIFY_LEAF;
  38. CRITICAL_SECTION NotifyLock;
  39. PDM_NOTIFY_BRANCH NotifyRoot=NULL;
  40. //
  41. // Local function prototypes
  42. //
  43. VOID
  44. DmpPruneBranch(
  45. IN PDM_NOTIFY_BRANCH Branch
  46. );
  47. PDM_NOTIFY_BRANCH
  48. DmpFindKeyInBranch(
  49. IN PDM_NOTIFY_BRANCH RootBranch,
  50. IN OUT LPCWSTR *RelativeName,
  51. OUT WORD *pNameLength
  52. );
  53. DWORD
  54. DmpAddNotifyLeaf(
  55. IN PDM_NOTIFY_BRANCH RootBranch,
  56. IN PDM_NOTIFY_LEAF NewLeaf,
  57. IN LPCWSTR RelativeName
  58. );
  59. VOID
  60. DmpReportNotifyWorker(
  61. IN PDM_NOTIFY_BRANCH RootBranch,
  62. IN LPCWSTR RelativeName,
  63. IN LPCWSTR FullName,
  64. IN DWORD Filter
  65. );
  66. BOOL
  67. DmpInitNotify(
  68. VOID
  69. )
  70. /*++
  71. Routine Description:
  72. Initializes the notification package for the DM.
  73. Arguments:
  74. None.
  75. Return Value:
  76. TRUE if successful
  77. FALSE otherwise
  78. --*/
  79. {
  80. InitializeCriticalSection(&NotifyLock);
  81. return(TRUE);
  82. }
  83. DWORD
  84. DmNotifyChangeKey(
  85. IN HDMKEY hKey,
  86. IN DWORD CompletionFilter,
  87. IN BOOL WatchTree,
  88. IN OPTIONAL PLIST_ENTRY ListHead,
  89. IN DM_NOTIFY_CALLBACK NotifyCallback,
  90. IN DWORD_PTR Context1,
  91. IN DWORD_PTR Context2
  92. )
  93. /*++
  94. Routine Description:
  95. Registers a notification for a specific registry key. When the
  96. notification event occurs, ApiReportRegistryNotify will be called.
  97. Arguments:
  98. hKey - Supplies the registry key handle on which the notification
  99. should be posted.
  100. CompletionFilter - Supplies the registry events which should trigger
  101. the notification.
  102. WatchTree - Supplies whether or not changes to the children of the specified
  103. key should trigger the notification.
  104. ListHead - If present, supplies the listhead that the new notification should be
  105. queued to. This listhead should be passed to DmRundownList.
  106. NotifyCallback - Supplies the notification routine that should be called
  107. when the notification occurs.
  108. Context1 - Supplies the first DWORD of context to be passed to ApiReportRegistryNotify
  109. Context2 - Supplies the second DWORD of context to be passed to ApiReportRegistryNotify
  110. Return Value:
  111. ERROR_SUCCESS if successful.
  112. Win32 error otherwise.
  113. --*/
  114. {
  115. PDMKEY Key;
  116. PDM_NOTIFY_LEAF Leaf;
  117. DWORD Status;
  118. Key = (PDMKEY)hKey;
  119. Leaf = LocalAlloc(LMEM_FIXED, sizeof(DM_NOTIFY_LEAF));
  120. if (Leaf == NULL) {
  121. return(ERROR_NOT_ENOUGH_MEMORY);
  122. }
  123. Leaf->hKey = hKey;
  124. Leaf->CompletionFilter = CompletionFilter;
  125. Leaf->WatchTree = WatchTree;
  126. Leaf->NotifyCallback = NotifyCallback;
  127. Leaf->Context1 = Context1;
  128. Leaf->Context2 = Context2;
  129. EnterCriticalSection(&NotifyLock);
  130. if (NotifyRoot == NULL) {
  131. //
  132. // Create notify root here.
  133. //
  134. NotifyRoot = LocalAlloc(LMEM_FIXED, sizeof(DM_NOTIFY_BRANCH));
  135. if (NotifyRoot == NULL) {
  136. LeaveCriticalSection(&NotifyLock);
  137. return(ERROR_NOT_ENOUGH_MEMORY);
  138. }
  139. InitializeListHead(&NotifyRoot->SiblingList);
  140. InitializeListHead(&NotifyRoot->ChildList);
  141. InitializeListHead(&NotifyRoot->LeafList);
  142. NotifyRoot->Parent = NULL;
  143. }
  144. Status = DmpAddNotifyLeaf(NotifyRoot, Leaf, Key->Name);
  145. if (Status == ERROR_SUCCESS) {
  146. InsertHeadList(&Key->NotifyList, &Leaf->KeyList);
  147. if (ARGUMENT_PRESENT(ListHead)) {
  148. InsertHeadList(ListHead, &Leaf->RundownList);
  149. } else {
  150. Leaf->RundownList.Flink = NULL;
  151. Leaf->RundownList.Blink = NULL;
  152. }
  153. } else {
  154. LocalFree(Leaf);
  155. }
  156. LeaveCriticalSection(&NotifyLock);
  157. return(Status);
  158. }
  159. VOID
  160. DmRundownList(
  161. IN PLIST_ENTRY ListHead
  162. )
  163. /*++
  164. Routine Description:
  165. Runs down a list of leaves. Used by the API when the notify port
  166. is closed.
  167. Arguments:
  168. ListHead - Supplies the head of the rundown list. This is the
  169. same listhead passed to DmNotifyChangeKey
  170. Return Value:
  171. None.
  172. --*/
  173. {
  174. PLIST_ENTRY ListEntry;
  175. PDM_NOTIFY_LEAF Leaf;
  176. //
  177. // Remove all outstanding DM_NOTIFY_LEAF structures from this list.
  178. //
  179. EnterCriticalSection(&NotifyLock);
  180. while (!IsListEmpty(ListHead)) {
  181. ListEntry = RemoveHeadList(ListHead);
  182. Leaf = CONTAINING_RECORD(ListEntry,
  183. DM_NOTIFY_LEAF,
  184. RundownList);
  185. RemoveEntryList(&Leaf->SiblingList);
  186. RemoveEntryList(&Leaf->KeyList);
  187. //
  188. // Attempt to prune this branch.
  189. //
  190. DmpPruneBranch(Leaf->Parent);
  191. LocalFree(Leaf);
  192. }
  193. LeaveCriticalSection(&NotifyLock);
  194. }
  195. VOID
  196. DmpRundownNotify(
  197. IN PDMKEY Key
  198. )
  199. /*++
  200. Routine Description:
  201. Cleans up any outstanding notifications for a key when the
  202. key is being closed.
  203. Arguments:
  204. Key - Supplies the key
  205. Return Value:
  206. None.
  207. --*/
  208. {
  209. PLIST_ENTRY ListEntry;
  210. PDM_NOTIFY_LEAF Leaf;
  211. //
  212. // Remove all outstanding DM_NOTIFY_LEAF structures from this key.
  213. //
  214. EnterCriticalSection(&NotifyLock);
  215. while (!IsListEmpty(&Key->NotifyList)) {
  216. ListEntry = RemoveHeadList(&Key->NotifyList);
  217. Leaf = CONTAINING_RECORD(ListEntry,
  218. DM_NOTIFY_LEAF,
  219. KeyList);
  220. RemoveEntryList(&Leaf->SiblingList);
  221. if (Leaf->RundownList.Flink != NULL) {
  222. RemoveEntryList(&Leaf->RundownList);
  223. }
  224. //
  225. // Attempt to prune this branch.
  226. //
  227. DmpPruneBranch(Leaf->Parent);
  228. LocalFree(Leaf);
  229. }
  230. LeaveCriticalSection(&NotifyLock);
  231. }
  232. VOID
  233. DmpPruneBranch(
  234. IN PDM_NOTIFY_BRANCH Branch
  235. )
  236. /*++
  237. Routine Description:
  238. Checks to see if a branch is empty and should be pruned (freed).
  239. If the branch is empty, this routine will recursively call itself
  240. on the parent until a non-empty branch is found.
  241. Arguments:
  242. Branch - Supplies the branch to be pruned.
  243. Return Value:
  244. None.
  245. --*/
  246. {
  247. if ((IsListEmpty(&Branch->ChildList)) &&
  248. (IsListEmpty(&Branch->LeafList))) {
  249. //
  250. // No need to keep this branch around any more. Remove
  251. // it from its parent, then check to see if the parent
  252. // should be pruned.
  253. //
  254. if (Branch->Parent == NULL) {
  255. //
  256. // This is the root, go ahead and free it up too.
  257. //
  258. CL_ASSERT(NotifyRoot == Branch);
  259. NotifyRoot = NULL;
  260. } else {
  261. RemoveEntryList(&Branch->SiblingList);
  262. DmpPruneBranch(Branch->Parent);
  263. }
  264. LocalFree(Branch);
  265. }
  266. }
  267. DWORD
  268. DmpAddNotifyLeaf(
  269. IN PDM_NOTIFY_BRANCH RootBranch,
  270. IN PDM_NOTIFY_LEAF NewLeaf,
  271. IN LPCWSTR RelativeName
  272. )
  273. /*++
  274. Routine Description:
  275. Adds a leaf to the notification key.
  276. If the RelativeName is empty, a leaf is created in RootBranch.
  277. If the RelativeName is not empty, look up its first component
  278. in RootBranch. If it's not there, create it. Then call ourselves
  279. recursively after stripping off the first component of RelativeName
  280. Arguments:
  281. RootBranch - Supplies the root where the leaf is to be added
  282. NewLeaf - Supplies the new leaf structure
  283. RelativeName - Supplies the relative name.
  284. Return Value:
  285. ERROR_SUCCESS if successful.
  286. Win32 error code otherwise.
  287. --*/
  288. {
  289. PLIST_ENTRY ListEntry;
  290. PDM_NOTIFY_BRANCH Branch;
  291. USHORT NameLength;
  292. LPCWSTR NextName;
  293. if (RelativeName[0] == '\0') {
  294. InsertHeadList(&RootBranch->LeafList, &NewLeaf->SiblingList);
  295. NewLeaf->Parent = RootBranch;
  296. return(ERROR_SUCCESS);
  297. }
  298. NextName = RelativeName;
  299. Branch = DmpFindKeyInBranch(RootBranch, &NextName, &NameLength);
  300. if (Branch == NULL) {
  301. //
  302. // No branch existed with this name. Create a new branch.
  303. //
  304. Branch = LocalAlloc(LMEM_FIXED, sizeof(DM_NOTIFY_BRANCH) + NameLength*sizeof(WCHAR));
  305. if (Branch == NULL) {
  306. return(ERROR_NOT_ENOUGH_MEMORY);
  307. }
  308. InitializeListHead(&Branch->ChildList);
  309. InitializeListHead(&Branch->LeafList);
  310. Branch->Parent = RootBranch;
  311. Branch->NameLength = NameLength;
  312. CopyMemory(Branch->KeyName, RelativeName, NameLength*sizeof(WCHAR));
  313. InsertHeadList(&RootBranch->ChildList, &Branch->SiblingList);
  314. }
  315. //
  316. // Call ourselves recursively on the new branch.
  317. //
  318. return(DmpAddNotifyLeaf(Branch, NewLeaf, NextName));
  319. }
  320. VOID
  321. DmpReportNotify(
  322. IN LPCWSTR KeyName,
  323. IN DWORD Filter
  324. )
  325. /*++
  326. Routine Description:
  327. Interface to the rest of DM to report a notification event on
  328. a particular key.
  329. Arguments:
  330. Key - Supplies the key that was modified.
  331. Filter - Supplies the modification type.
  332. Return Value:
  333. None.
  334. --*/
  335. {
  336. if (NotifyRoot == NULL) {
  337. return;
  338. }
  339. EnterCriticalSection(&NotifyLock);
  340. if (NotifyRoot != NULL) {
  341. DmpReportNotifyWorker(NotifyRoot,
  342. KeyName,
  343. KeyName,
  344. Filter);
  345. }
  346. LeaveCriticalSection(&NotifyLock);
  347. }
  348. VOID
  349. DmpReportNotifyWorker(
  350. IN PDM_NOTIFY_BRANCH RootBranch,
  351. IN LPCWSTR RelativeName,
  352. IN LPCWSTR FullName,
  353. IN DWORD Filter
  354. )
  355. /*++
  356. Routine Description:
  357. Recursive worker routine that drills down through the notification
  358. tree until it reaches the supplied name. Notifications are issued
  359. for any leaves along the path that match the event.
  360. Arguments:
  361. RootBranch - Supplies the branch of the tree to start with.
  362. RelativeName - Supplies the name of the changed key, relative to Branch.
  363. FullName - Supplies the full name of the changed key.
  364. Filter - Supplies the type of event.
  365. Return Value:
  366. None.
  367. --*/
  368. {
  369. PLIST_ENTRY ListEntry;
  370. PDM_NOTIFY_LEAF Leaf;
  371. PDM_NOTIFY_BRANCH Branch;
  372. LPCWSTR NextName;
  373. WORD Dummy;
  374. //
  375. // First, issue notifies for any leaves at this node
  376. //
  377. ListEntry = RootBranch->LeafList.Flink;
  378. while (ListEntry != &RootBranch->LeafList) {
  379. Leaf = CONTAINING_RECORD(ListEntry,
  380. DM_NOTIFY_LEAF,
  381. SiblingList);
  382. if (Leaf->CompletionFilter & Filter) {
  383. if ( Leaf->WatchTree ||
  384. (RelativeName[0] == '\0')) {
  385. (Leaf->NotifyCallback)(Leaf->Context1,
  386. Leaf->Context2,
  387. Filter,
  388. RelativeName);
  389. }
  390. }
  391. ListEntry = ListEntry->Flink;
  392. }
  393. //
  394. // Now search the child list for a subkey that matches the next component
  395. // of the key name. If there isn't one, we are done. If there is one,
  396. // call ourselves recursively on it.
  397. //
  398. if (RelativeName[0] == '\0') {
  399. return;
  400. }
  401. NextName = RelativeName;
  402. Branch = DmpFindKeyInBranch(RootBranch, &NextName, &Dummy);
  403. if (Branch != NULL) {
  404. DmpReportNotifyWorker(Branch, NextName, FullName, Filter);
  405. }
  406. }
  407. PDM_NOTIFY_BRANCH
  408. DmpFindKeyInBranch(
  409. IN PDM_NOTIFY_BRANCH RootBranch,
  410. IN OUT LPCWSTR *RelativeName,
  411. OUT WORD *pNameLength
  412. )
  413. /*++
  414. Routine Description:
  415. Finds the next component of a key name in a branch.
  416. Arguments:
  417. RootBranch - Supplies the branch to search.
  418. RelativeName - Supplies the relative name of the key.
  419. Returns the remaining name
  420. NameLength - Returns the length of the next component.
  421. Return Value:
  422. Pointer to the found branch if successful.
  423. NULL otherwise.
  424. --*/
  425. {
  426. PDM_NOTIFY_BRANCH Branch;
  427. USHORT NameLength;
  428. LPCWSTR NextName;
  429. PLIST_ENTRY ListEntry;
  430. //
  431. // Find the first component of the relative name.
  432. //
  433. NextName = wcschr(*RelativeName, '\\');
  434. if (NextName==NULL) {
  435. NameLength = (USHORT)lstrlenW(*RelativeName);
  436. NextName = *RelativeName + NameLength;
  437. } else {
  438. NameLength = (USHORT)(NextName - *RelativeName);
  439. ++NextName;
  440. }
  441. *pNameLength = NameLength;
  442. //
  443. // Search through the root's children to try and find a match on the
  444. // first component.
  445. //
  446. ListEntry = RootBranch->ChildList.Flink;
  447. while (ListEntry != &RootBranch->ChildList) {
  448. Branch = CONTAINING_RECORD(ListEntry,
  449. DM_NOTIFY_BRANCH,
  450. SiblingList);
  451. if ((NameLength == Branch->NameLength) &&
  452. (wcsncmp(*RelativeName, Branch->KeyName, NameLength)==0)) {
  453. //
  454. // We have matched an existing branch. Return success.
  455. //
  456. *RelativeName = NextName;
  457. return(Branch);
  458. }
  459. ListEntry = ListEntry->Flink;
  460. }
  461. *RelativeName = NextName;
  462. return(NULL);
  463. }