xref: /reactos/ntoskrnl/config/cmhook.c (revision 64a77203)
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 }
198