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