* Sync with trunk r64401.
[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 INIT_FUNCTION
38 CmpInitCallback(VOID)
39 {
40 ULONG i;
41 PAGED_CODE();
42
43 /* Reset counter */
44 CmpCallBackCount = 0;
45
46 /* Loop all the callbacks */
47 for (i = 0; i < CMP_MAX_CALLBACKS; i++)
48 {
49 /* Initialize this one */
50 ExInitializeCallBack(&CmpCallBackVector[i]);
51 }
52
53 /* ROS: Initialize old-style callbacks for now */
54 InitializeListHead(&CmiCallbackHead);
55 ExInitializeFastMutex(&CmiCallbackLock);
56 }
57
58 NTSTATUS
59 CmiCallRegisteredCallbacks(IN REG_NOTIFY_CLASS Argument1,
60 IN PVOID Argument2)
61 {
62 PLIST_ENTRY CurrentEntry;
63 NTSTATUS Status = STATUS_SUCCESS;
64 PREGISTRY_CALLBACK CurrentCallback;
65 PAGED_CODE();
66
67 ExAcquireFastMutex(&CmiCallbackLock);
68
69 for (CurrentEntry = CmiCallbackHead.Flink;
70 CurrentEntry != &CmiCallbackHead;
71 CurrentEntry = CurrentEntry->Flink)
72 {
73 CurrentCallback = CONTAINING_RECORD(CurrentEntry, REGISTRY_CALLBACK, ListEntry);
74 if (!CurrentCallback->PendingDelete &&
75 ExAcquireRundownProtection(&CurrentCallback->RundownRef))
76 {
77 /* don't hold locks during the callbacks! */
78 ExReleaseFastMutex(&CmiCallbackLock);
79
80 Status = CurrentCallback->Function(CurrentCallback->Context,
81 (PVOID)Argument1,
82 Argument2);
83
84 ExAcquireFastMutex(&CmiCallbackLock);
85
86 /* don't release the rundown protection before holding the callback lock
87 so the pointer to the next callback isn't cleared in case this callback
88 get's deleted */
89 ExReleaseRundownProtection(&CurrentCallback->RundownRef);
90 if(!NT_SUCCESS(Status))
91 {
92 /* one callback returned failure, don't call any more callbacks */
93 break;
94 }
95 }
96 }
97
98 ExReleaseFastMutex(&CmiCallbackLock);
99
100 return Status;
101 }
102
103 /* PUBLIC FUNCTIONS **********************************************************/
104
105 /*
106 * @implemented
107 */
108 NTSTATUS
109 NTAPI
110 CmRegisterCallback(IN PEX_CALLBACK_FUNCTION Function,
111 IN PVOID Context,
112 IN OUT PLARGE_INTEGER Cookie)
113 {
114 PREGISTRY_CALLBACK Callback;
115 PAGED_CODE();
116 ASSERT(Function && Cookie);
117
118 Callback = ExAllocatePoolWithTag(PagedPool,
119 sizeof(REGISTRY_CALLBACK),
120 'bcMC');
121 if (Callback == NULL)
122 {
123 return STATUS_INSUFFICIENT_RESOURCES;
124 }
125
126 /* initialize the callback */
127 ExInitializeRundownProtection(&Callback->RundownRef);
128 Callback->Function = Function;
129 Callback->Context = Context;
130 Callback->PendingDelete = FALSE;
131
132 /* add it to the callback list and receive a cookie for the callback */
133 ExAcquireFastMutex(&CmiCallbackLock);
134
135 /* FIXME - to receive a unique cookie we'll just return the pointer to the
136 callback object */
137 Callback->Cookie.QuadPart = (ULONG_PTR)Callback;
138 InsertTailList(&CmiCallbackHead, &Callback->ListEntry);
139
140 ExReleaseFastMutex(&CmiCallbackLock);
141
142 *Cookie = Callback->Cookie;
143 return STATUS_SUCCESS;
144 }
145
146 /*
147 * @implemented
148 */
149 NTSTATUS
150 NTAPI
151 CmUnRegisterCallback(IN LARGE_INTEGER Cookie)
152 {
153 PLIST_ENTRY CurrentEntry;
154 PREGISTRY_CALLBACK CurrentCallback;
155 PAGED_CODE();
156
157 ExAcquireFastMutex(&CmiCallbackLock);
158
159 for (CurrentEntry = CmiCallbackHead.Flink;
160 CurrentEntry != &CmiCallbackHead;
161 CurrentEntry = CurrentEntry->Flink)
162 {
163 CurrentCallback = CONTAINING_RECORD(CurrentEntry, REGISTRY_CALLBACK, ListEntry);
164 if (CurrentCallback->Cookie.QuadPart == Cookie.QuadPart)
165 {
166 if (!CurrentCallback->PendingDelete)
167 {
168 /* found the callback, don't unlink it from the list yet so we don't screw
169 the calling loop */
170 CurrentCallback->PendingDelete = TRUE;
171 ExReleaseFastMutex(&CmiCallbackLock);
172
173 /* if the callback is currently executing, wait until it finished */
174 ExWaitForRundownProtectionRelease(&CurrentCallback->RundownRef);
175
176 /* time to unlink it. It's now safe because every attempt to acquire a
177 runtime protection on this callback will fail */
178 ExAcquireFastMutex(&CmiCallbackLock);
179 RemoveEntryList(&CurrentCallback->ListEntry);
180 ExReleaseFastMutex(&CmiCallbackLock);
181
182 /* free the callback */
183 ExFreePoolWithTag(CurrentCallback, 'bcMC');
184 return STATUS_SUCCESS;
185 }
186 else
187 {
188 /* pending delete, pretend like it already is deleted */
189 ExReleaseFastMutex(&CmiCallbackLock);
190 return STATUS_UNSUCCESSFUL;
191 }
192 }
193 }
194
195 ExReleaseFastMutex(&CmiCallbackLock);
196
197 return STATUS_UNSUCCESSFUL;
198 }