xref: /reactos/ntoskrnl/config/cmhook.c (revision 7eead935)
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 INIT_FUNCTION
36 VOID
37 NTAPI
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 }
199