fa0e9cf1ec514cb3036dd48fc57d256172363a80
[reactos.git] / reactos / ntoskrnl / ex / callback.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ex/callback.c
6 * PURPOSE: Executive callbacks
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 * Alex Ionescu (alex@relsoft.net)
10 */
11
12
13 /*
14 * NOTE: These funtions are not implemented in NT4, but
15 * they are implemented in Win2k.
16 */
17
18 /* INCLUDES *****************************************************************/
19
20 #include <ntoskrnl.h>
21 #include <internal/callbacks.h>
22 #include <internal/debug.h>
23
24 /* FUNCTIONS *****************************************************************/
25
26 /*
27 * ExpInitializeCallbacks
28 *
29 * FUNCTION:
30 * Creates the Callback Object as a valid Object Type in the Kernel.
31 *
32 * ARGUMENTS:
33 * None
34 *
35 * RETURNS:
36 * TRUE if the Callback Object Type was successfully created.
37 */
38 VOID
39 ExpInitializeCallbacks(VOID)
40 {
41 OBJECT_ATTRIBUTES ObjectAttributes;
42 NTSTATUS Status;
43 UNICODE_STRING DirName;
44 UNICODE_STRING CallbackName;
45 UNICODE_STRING Name;
46 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
47 HANDLE DirectoryHandle;
48 ULONG i;
49
50 /* Initialize the Callback Object type */
51 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
52 RtlInitUnicodeString(&Name, L"Callback");
53 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
54 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(_INT_CALLBACK_OBJECT);
55 ObjectTypeInitializer.GenericMapping = ExpCallbackMapping;
56 ObjectTypeInitializer.PoolType = NonPagedPool;
57 ObjectTypeInitializer.UseDefaultObject = TRUE;
58
59 Status = ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &ExCallbackObjectType);
60
61 /* Fail if it wasn't created successfully */
62 if (!NT_SUCCESS(Status))
63 {
64 return;
65 }
66
67 /* Initialize the Object */
68 RtlRosInitUnicodeStringFromLiteral(&DirName, L"\\Callback" );
69 InitializeObjectAttributes(
70 &ObjectAttributes,
71 &DirName,
72 OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
73 NULL,
74 NULL
75 );
76
77 /* Create the Object Directory */
78 Status = NtCreateDirectoryObject(
79 &DirectoryHandle,
80 DIRECTORY_ALL_ACCESS,
81 &ObjectAttributes
82 );
83
84 /* Fail if couldn't create */
85 if (!NT_SUCCESS(Status))
86 {
87 return;
88 }
89
90 /* Close Handle... */
91 NtClose(DirectoryHandle);
92
93 /* Initialize Event used when unregistering */
94 KeInitializeEvent(&ExpCallbackEvent, NotificationEvent, 0);
95
96 /* Default NT Kernel Callbacks. */
97 for (i=0; ExpInitializeCallback[i].CallbackObject; i++)
98 {
99 /* Create the name from the structure */
100 RtlInitUnicodeString(&CallbackName, ExpInitializeCallback[i].Name);
101
102 /* Initialize the Object Attributes Structure */
103 InitializeObjectAttributes(
104 &ObjectAttributes,
105 &CallbackName,
106 OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
107 NULL,
108 NULL
109 );
110
111 /* Create the Callback Object */
112 Status = ExCreateCallback(
113 (PCALLBACK_OBJECT*)&(ExpInitializeCallback[i].CallbackObject),
114 &ObjectAttributes,
115 TRUE,
116 TRUE
117 );
118
119 /* Make sure Global Callbacks have been created */
120 if (!NT_SUCCESS(Status))
121 {
122 return;
123 }
124 }
125 /* Everything successful */
126 }
127
128 /*
129 * ExCreateCallback
130 *
131 * FUNCTION:
132 * Opens or creates a Callback Object. Creates only if Create is true.
133 * Allows multiple Callback Functions to be registred only if AllowMultipleCallbacks
134 * is true.
135 *
136 * ARGUMENTS:
137 * CallbackObject = Pointer that will receive the Callback Object.
138 * CallbackName = Name of Callback
139 * Create = Determines if the object will be created if it doesn't exit
140 * AllowMultipleCallbacks = Determines if more then one registered callback function
141 * can be attached to this Callback Object.
142 *
143 * RETURNS:
144 * STATUS_SUCESS if not failed.
145 *
146 * @implemented
147 */
148 NTSTATUS
149 STDCALL
150 ExCreateCallback(
151 OUT PCALLBACK_OBJECT *CallbackObject,
152 IN POBJECT_ATTRIBUTES ObjectAttributes,
153 IN BOOLEAN Create,
154 IN BOOLEAN AllowMultipleCallbacks
155 )
156 {
157 PINT_CALLBACK_OBJECT Callback;
158 NTSTATUS Status;
159 HANDLE Handle;
160
161 PAGED_CODE();
162
163 /* Open a handle to the callback if it exists */
164 if (ObjectAttributes->ObjectName)
165 {
166 Status = ObOpenObjectByName(ObjectAttributes,
167 ExCallbackObjectType,
168 NULL,
169 KernelMode,
170 0,
171 NULL,
172 &Handle);
173 }
174 else
175 {
176 Status = STATUS_UNSUCCESSFUL;
177 }
178
179 /* We weren't able to open it...should we create it? */
180 if(!NT_SUCCESS(Status) && Create )
181 {
182 Status = ObCreateObject(KernelMode,
183 ExCallbackObjectType,
184 ObjectAttributes,
185 KernelMode,
186 NULL,
187 sizeof(_INT_CALLBACK_OBJECT),
188 0,
189 0,
190 (PVOID *)&Callback );
191
192 /* We Created it...let's initialize the structure now */
193 if(NT_SUCCESS(Status))
194 {
195 KeInitializeSpinLock (&Callback->Lock); /* SpinLock */
196 InitializeListHead(&Callback->RegisteredCallbacks); /* Callback Entries */
197 Callback->AllowMultipleCallbacks = AllowMultipleCallbacks; /* Multiple Callbacks */
198 Status = ObInsertObject ( /* Create the object */
199 Callback,
200 NULL,
201 FILE_READ_DATA,
202 0,
203 NULL,
204 &Handle );
205 }
206 }
207 if(NT_SUCCESS(Status))
208 {
209
210 /* Get a pointer to the new object from the handle we just got */
211 Status = ObReferenceObjectByHandle (
212 Handle,
213 0,
214 ExCallbackObjectType,
215 KernelMode,
216 (PVOID)&Callback,
217 NULL
218 );
219 /* Close the Handle, since we now have the pointer */
220 ZwClose(Handle);
221 }
222
223 /* Everything went fine, so return a pointer to the Object */
224 if (NT_SUCCESS(Status))
225 {
226 *CallbackObject = (PCALLBACK_OBJECT)Callback;
227 }
228 return Status;
229 }
230
231 /*
232 * ExNotifyCallback
233 *
234 * FUNCTION:
235 * Calls a function pointer (a registered callback)
236 *
237 * ARGUMENTS:
238 * CallbackObject - Which callback to call
239 * Argument1 - Pointer/data to send to callback function
240 * Argument2 - Pointer/data to send to callback function
241 *
242 * RETURNS:
243 * Nothing
244 *
245 * @implemented
246 */
247 VOID
248 STDCALL
249 ExNotifyCallback(
250 IN PCALLBACK_OBJECT OpaqueCallbackObject,
251 IN PVOID Argument1,
252 IN PVOID Argument2
253 )
254 {
255 PINT_CALLBACK_OBJECT CallbackObject = (PINT_CALLBACK_OBJECT)OpaqueCallbackObject;
256 PLIST_ENTRY RegisteredCallbacks;
257 PCALLBACK_REGISTRATION CallbackRegistration;
258 KIRQL OldIrql;
259
260 /* Acquire the Lock */
261 OldIrql = KfAcquireSpinLock(&CallbackObject->Lock);
262
263 /* Enumerate through all the registered functions */
264 for (RegisteredCallbacks = CallbackObject->RegisteredCallbacks.Flink;
265 RegisteredCallbacks != &CallbackObject->RegisteredCallbacks;
266 RegisteredCallbacks = RegisteredCallbacks->Flink)
267 {
268
269 /* Get a pointer to a Callback Registration from the List Entries */
270 CallbackRegistration = CONTAINING_RECORD ( RegisteredCallbacks,
271 CALLBACK_REGISTRATION,
272 RegisteredCallbacks);
273
274 /* Don't bother doing Callback Notification if it's pending to be deleted */
275 if (!CallbackRegistration->PendingDeletion)
276 {
277
278 /* Mark the Callback in use, so it won't get deleted while we are calling it */
279 CallbackRegistration->InUse += 1;
280
281 /* Release the Spinlock before making the call */
282 KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
283
284 /* Call the Registered Function */
285 CallbackRegistration->CallbackFunction (
286 CallbackRegistration->CallbackContext,
287 Argument1,
288 Argument2
289 );
290
291 /* Get SpinLock back */
292 OldIrql = KfAcquireSpinLock(&CallbackObject->Lock);
293
294 /* We are not in use anymore */
295 CallbackRegistration->InUse -= 1;
296
297 /* If another instance of this function isn't running and deletion is pending, signal the event */
298 if (CallbackRegistration->PendingDeletion && CallbackRegistration->InUse == 0)
299 {
300 KeSetEvent(&ExpCallbackEvent, 0, FALSE);
301 }
302 }
303 }
304 /* Unsynchronize and release the Callback Object */
305 KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
306 }
307
308 /*
309 * ExRegisterCallback
310 *
311 * FUNCTION:
312 * Allows a function to associate a callback pointer (Function)
313 * to a created Callback object
314 *
315 * ARGUMENTS:
316 * CallbackObject = The Object Created with ExCreateCallBack
317 * CallBackFunction = Pointer to the function to be called back
318 * CallBackContext = Block of memory that can contain user-data
319 * which will be passed on to the callback
320 *
321 * RETURNS:
322 * A handle to a Callback Registration Structure (MSDN Documentation)
323 *
324 * @implemented
325 */
326 PVOID
327 STDCALL
328 ExRegisterCallback(
329 IN PCALLBACK_OBJECT OpaqueCallbackObject,
330 IN PCALLBACK_FUNCTION CallbackFunction,
331 IN PVOID CallbackContext
332 )
333 {
334 PINT_CALLBACK_OBJECT CallbackObject = (PINT_CALLBACK_OBJECT)OpaqueCallbackObject;
335 PCALLBACK_REGISTRATION CallbackRegistration = NULL;
336 KIRQL OldIrql;
337
338 PAGED_CODE();
339
340 /* Create reference to Callback Object */
341 ObReferenceObject (CallbackObject);
342
343 /* Allocate memory for the structure */
344 CallbackRegistration = ExAllocatePoolWithTag(
345 NonPagedPool,
346 sizeof(CallbackRegistration),
347 CALLBACK_TAG
348 );
349 /* Fail if memory allocation failed */
350 if(!CallbackRegistration)
351 {
352 ObDereferenceObject (CallbackObject);
353 return NULL;
354 }
355
356 /* Create Callback Registration */
357 CallbackRegistration->CallbackObject = CallbackObject; /* When unregistering, drivers send a handle to the Registration, not the object... */
358 CallbackRegistration->CallbackFunction = CallbackFunction; /* NotifyCallback uses Objects, so this needs to be here in order to call the registered functions */
359 CallbackRegistration->CallbackContext = CallbackContext; /* The documented NotifyCallback returns the Context, so we must save this somewhere */
360
361 /* Acquire SpinLock */
362 OldIrql = KfAcquireSpinLock (&CallbackObject->Lock);
363
364 /* Add Callback if 1) No Callbacks registered or 2) Multiple Callbacks allowed */
365 if(CallbackObject->AllowMultipleCallbacks || IsListEmpty(&CallbackObject->RegisteredCallbacks))
366 {
367 InsertTailList(&CallbackObject->RegisteredCallbacks,&CallbackRegistration->RegisteredCallbacks);
368 }
369 else
370 {
371 ExFreePool(CallbackRegistration);
372 CallbackRegistration = NULL;
373 }
374
375 /* Release SpinLock */
376 KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
377
378 /* Return handle to Registration Object */
379 return (PVOID) CallbackRegistration;
380 }
381
382 /*
383 * ExUnregisterCallback
384 *
385 * FUNCTION:
386 * Deregisters a CallBack
387 *
388 * ARGUMENTS:
389 * CallbackRegistration = Callback Registration Handle
390 *
391 * RETURNS:
392 * Nothing
393 *
394 * @implemented
395 */
396 VOID STDCALL
397 ExUnregisterCallback(
398 IN PVOID CallbackRegistrationHandle
399 )
400 {
401 PCALLBACK_REGISTRATION CallbackRegistration;
402 PINT_CALLBACK_OBJECT CallbackObject;
403 KIRQL OldIrql;
404
405 PAGED_CODE();
406
407 /* Convert Handle to valid Structure Pointer */
408 CallbackRegistration = (PCALLBACK_REGISTRATION) CallbackRegistrationHandle;
409
410 /* Get the Callback Object */
411 CallbackObject = CallbackRegistration->CallbackObject;
412
413 /* Lock the Object */
414 OldIrql = KfAcquireSpinLock (&CallbackObject->Lock);
415
416 /* We can't Delete the Callback if it's in use, because this would create a call towards a null pointer => crash */
417 while (CallbackRegistration->InUse)
418 {
419
420 /* Similarly, we also don't want to wait ages for all pending callbacks to be called */
421 CallbackRegistration->PendingDeletion = TRUE;
422
423 /* We are going to wait for the event, so the Lock isn't necessary */
424 KfReleaseSpinLock (&CallbackObject->Lock, OldIrql);
425
426 /* Make sure the event is cleared */
427 KeClearEvent (&ExpCallbackEvent);
428
429 /* Wait for the Event */
430 KeWaitForSingleObject (
431 &ExpCallbackEvent,
432 Executive,
433 KernelMode,
434 FALSE,
435 NULL
436 );
437
438 /* We need the Lock again */
439 OldIrql = KfAcquireSpinLock(&CallbackObject->Lock);
440 }
441
442 /* Remove the Callback */
443 RemoveEntryList(&CallbackRegistration->RegisteredCallbacks);
444
445 /* It's now safe to release the lock */
446 KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
447
448 /* Delete this registration */
449 ExFreePool(CallbackRegistration);
450
451 /* Remove the reference */
452 ObDereferenceObject(CallbackObject);
453 }
454
455 /* EOF */