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