sync with trunk r46493
[reactos.git] / ntoskrnl / config / cmhook.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/cmhook.c
5 * PURPOSE: Configuration Manager - Registry Notifications/Callbacks
6 * PROGRAMMERS: Thomas Weidenmueller (w3seek@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "ntoskrnl.h"
12 #define NDEBUG
13 #include "debug.h"
14
15 /* GLOBALS *******************************************************************/
16
17 ULONG CmpCallBackCount = 0;
18 EX_CALLBACK CmpCallBackVector[100];
19
20 LIST_ENTRY CmiCallbackHead;
21 FAST_MUTEX CmiCallbackLock;
22
23 typedef struct _REGISTRY_CALLBACK
24 {
25 LIST_ENTRY ListEntry;
26 EX_RUNDOWN_REF RundownRef;
27 PEX_CALLBACK_FUNCTION Function;
28 PVOID Context;
29 LARGE_INTEGER Cookie;
30 BOOLEAN PendingDelete;
31 } REGISTRY_CALLBACK, *PREGISTRY_CALLBACK;
32
33 /* PRIVATE FUNCTIONS *********************************************************/
34
35 VOID
36 NTAPI
37 CmpInitCallback(VOID)
38 {
39 ULONG i;
40 PAGED_CODE();
41
42 /* Reset counter */
43 CmpCallBackCount = 0;
44
45 /* Loop all the callbacks */
46 for (i = 0; i < CMP_MAX_CALLBACKS; i++)
47 {
48 /* Initialize this one */
49 ExInitializeCallBack(&CmpCallBackVector[i]);
50 }
51
52 /* ROS: Initialize old-style callbacks for now */
53 InitializeListHead(&CmiCallbackHead);
54 ExInitializeFastMutex(&CmiCallbackLock);
55 }
56
57 NTSTATUS
58 CmiCallRegisteredCallbacks(IN REG_NOTIFY_CLASS Argument1,
59 IN PVOID Argument2)
60 {
61 PLIST_ENTRY CurrentEntry;
62 NTSTATUS Status = STATUS_SUCCESS;
63 PREGISTRY_CALLBACK CurrentCallback;
64 PAGED_CODE();
65
66 ExAcquireFastMutex(&CmiCallbackLock);
67
68 for (CurrentEntry = CmiCallbackHead.Flink;
69 CurrentEntry != &CmiCallbackHead;
70 CurrentEntry = CurrentEntry->Flink)
71 {
72 CurrentCallback = CONTAINING_RECORD(CurrentEntry, REGISTRY_CALLBACK, ListEntry);
73 if (!CurrentCallback->PendingDelete &&
74 ExAcquireRundownProtection(&CurrentCallback->RundownRef))
75 {
76 /* don't hold locks during the callbacks! */
77 ExReleaseFastMutex(&CmiCallbackLock);
78
79 Status = CurrentCallback->Function(CurrentCallback->Context,
80 (PVOID)Argument1,
81 Argument2);
82
83 ExAcquireFastMutex(&CmiCallbackLock);
84
85 /* don't release the rundown protection before holding the callback lock
86 so the pointer to the next callback isn't cleared in case this callback
87 get's deleted */
88 ExReleaseRundownProtection(&CurrentCallback->RundownRef);
89 if(!NT_SUCCESS(Status))
90 {
91 /* one callback returned failure, don't call any more callbacks */
92 break;
93 }
94 }
95 }
96
97 ExReleaseFastMutex(&CmiCallbackLock);
98
99 return Status;
100 }
101
102 /* PUBLIC FUNCTIONS **********************************************************/
103
104 /*
105 * @implemented
106 */
107 NTSTATUS
108 NTAPI
109 CmRegisterCallback(IN PEX_CALLBACK_FUNCTION Function,
110 IN PVOID Context,
111 IN OUT PLARGE_INTEGER Cookie)
112 {
113 PREGISTRY_CALLBACK Callback;
114 PAGED_CODE();
115 ASSERT(Function && Cookie);
116
117 Callback = ExAllocatePoolWithTag(PagedPool,
118 sizeof(REGISTRY_CALLBACK),
119 'bcMC');
120 if (Callback != NULL)
121 {
122 /* initialize the callback */
123 ExInitializeRundownProtection(&Callback->RundownRef);
124 Callback->Function = Function;
125 Callback->Context = Context;
126 Callback->PendingDelete = FALSE;
127
128 /* add it to the callback list and receive a cookie for the callback */
129 ExAcquireFastMutex(&CmiCallbackLock);
130
131 /* FIXME - to receive a unique cookie we'll just return the pointer to the
132 callback object */
133 Callback->Cookie.QuadPart = (ULONG_PTR)Callback;
134 InsertTailList(&CmiCallbackHead, &Callback->ListEntry);
135
136 ExReleaseFastMutex(&CmiCallbackLock);
137
138 *Cookie = Callback->Cookie;
139 return STATUS_SUCCESS;
140 }
141
142 return STATUS_INSUFFICIENT_RESOURCES;
143 }
144
145 /*
146 * @implemented
147 */
148 NTSTATUS
149 NTAPI
150 CmUnRegisterCallback(IN LARGE_INTEGER Cookie)
151 {
152 PLIST_ENTRY CurrentEntry;
153 PREGISTRY_CALLBACK CurrentCallback;
154 PAGED_CODE();
155
156 ExAcquireFastMutex(&CmiCallbackLock);
157
158 for (CurrentEntry = CmiCallbackHead.Flink;
159 CurrentEntry != &CmiCallbackHead;
160 CurrentEntry = CurrentEntry->Flink)
161 {
162 CurrentCallback = CONTAINING_RECORD(CurrentEntry, REGISTRY_CALLBACK, ListEntry);
163 if (CurrentCallback->Cookie.QuadPart == Cookie.QuadPart)
164 {
165 if (!CurrentCallback->PendingDelete)
166 {
167 /* found the callback, don't unlink it from the list yet so we don't screw
168 the calling loop */
169 CurrentCallback->PendingDelete = TRUE;
170 ExReleaseFastMutex(&CmiCallbackLock);
171
172 /* if the callback is currently executing, wait until it finished */
173 ExWaitForRundownProtectionRelease(&CurrentCallback->RundownRef);
174
175 /* time to unlink it. It's now safe because every attempt to acquire a
176 runtime protection on this callback will fail */
177 ExAcquireFastMutex(&CmiCallbackLock);
178 RemoveEntryList(&CurrentCallback->ListEntry);
179 ExReleaseFastMutex(&CmiCallbackLock);
180
181 /* free the callback */
182 ExFreePool(CurrentCallback);
183 return STATUS_SUCCESS;
184 }
185 else
186 {
187 /* pending delete, pretend like it already is deleted */
188 ExReleaseFastMutex(&CmiCallbackLock);
189 return STATUS_UNSUCCESSFUL;
190 }
191 }
192 }
193
194 ExReleaseFastMutex(&CmiCallbackLock);
195
196 return STATUS_UNSUCCESSFUL;
197 }