f49e0a6550f534afd4ecf55f76fbf18f7e30dcae
[reactos.git] / reactos / ntoskrnl / lpc / send.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/send.c
5 * PURPOSE: Local Procedure Call: Sending (Requests)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #include "lpc.h"
13 #define NDEBUG
14 #include <internal/debug.h>
15
16 /* PUBLIC FUNCTIONS **********************************************************/
17
18 /*
19 * @implemented
20 */
21 NTSTATUS
22 NTAPI
23 LpcRequestPort(IN PVOID PortObject,
24 IN PPORT_MESSAGE LpcMessage)
25 {
26 PLPCP_PORT_OBJECT Port = (PLPCP_PORT_OBJECT)PortObject, QueuePort;
27 ULONG MessageType;
28 PLPCP_MESSAGE Message;
29 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
30 PAGED_CODE();
31 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", Port, LpcMessage);
32
33 /* Check if this is a non-datagram message */
34 if (LpcMessage->u2.s2.Type)
35 {
36 /* Get the message type */
37 MessageType = LpcpGetMessageType(LpcMessage);
38
39 /* Validate it */
40 if ((MessageType < LPC_DATAGRAM) || (MessageType > LPC_CLIENT_DIED))
41 {
42 /* Fail */
43 return STATUS_INVALID_PARAMETER;
44 }
45
46 /* Mark this as a kernel-mode message only if we really came from there */
47 if ((PreviousMode == KernelMode) &&
48 (LpcMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE))
49 {
50 /* We did, this is a kernel mode message */
51 MessageType |= LPC_KERNELMODE_MESSAGE;
52 }
53 }
54 else
55 {
56 /* This is a datagram */
57 MessageType = LPC_DATAGRAM;
58 }
59
60 /* Can't have data information on this type of call */
61 if (LpcMessage->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER;
62
63 /* Validate message sizes */
64 if ((LpcMessage->u1.s1.TotalLength > Port->MaxMessageLength) ||
65 (LpcMessage->u1.s1.TotalLength <= LpcMessage->u1.s1.DataLength))
66 {
67 /* Fail */
68 return STATUS_PORT_MESSAGE_TOO_LONG;
69 }
70
71 /* Allocate a new message */
72 Message = LpcpAllocateFromPortZone();
73 if (!Message) return STATUS_NO_MEMORY;
74
75 /* Clear the context */
76 Message->PortContext = NULL;
77
78 /* Copy the message */
79 LpcpMoveMessage(&Message->Request,
80 LpcMessage,
81 LpcMessage + 1,
82 MessageType,
83 &PsGetCurrentThread()->Cid);
84
85 /* Acquire the LPC lock */
86 KeAcquireGuardedMutex(&LpcpLock);
87
88 /* Check if this is anything but a connection port */
89 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
90 {
91 /* The queue port is the connected port */
92 QueuePort = Port->ConnectedPort;
93 if (QueuePort)
94 {
95 /* Check if this is a client port */
96 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
97 {
98 /* Then copy the context */
99 Message->PortContext = QueuePort->PortContext;
100 QueuePort = Port->ConnectionPort;
101 }
102 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
103 {
104 /* Any other kind of port, use the connection port */
105 QueuePort = Port->ConnectionPort;
106 }
107 }
108 }
109 else
110 {
111 /* For connection ports, use the port itself */
112 QueuePort = PortObject;
113 }
114
115 /* Make sure we have a port */
116 if (QueuePort)
117 {
118 /* Generate the Message ID and set it */
119 Message->Request.MessageId = LpcpNextMessageId++;
120 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
121 Message->Request.CallbackId = 0;
122
123 /* No Message ID for the thread */
124 PsGetCurrentThread()->LpcReplyMessageId = 0;
125
126 /* Insert the message in our chain */
127 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
128
129 /* Release the lock and release the semaphore */
130 KeReleaseGuardedMutex(&LpcpLock);
131 LpcpCompleteWait(QueuePort->MsgQueue.Semaphore);
132
133 /* If this is a waitable port, wake it up */
134 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
135 {
136 /* Wake it */
137 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
138 }
139
140 /* We're done */
141 LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message);
142 return STATUS_SUCCESS;
143 }
144
145 /* If we got here, then free the message and fail */
146 LpcpFreeToPortZone(Message, TRUE);
147 KeReleaseGuardedMutex(&LpcpLock);
148 return STATUS_PORT_DISCONNECTED;
149 }
150
151 /*
152 * @unimplemented
153 */
154 NTSTATUS
155 NTAPI
156 LpcRequestWaitReplyPort(IN PVOID Port,
157 IN PPORT_MESSAGE LpcMessageRequest,
158 OUT PPORT_MESSAGE LpcMessageReply)
159 {
160 UNIMPLEMENTED;
161 return STATUS_NOT_IMPLEMENTED;
162 }
163
164 /*
165 * @unimplemented
166 */
167 NTSTATUS
168 NTAPI
169 NtRequestPort(IN HANDLE PortHandle,
170 IN PPORT_MESSAGE LpcMessage)
171 {
172 UNIMPLEMENTED;
173 return STATUS_NOT_IMPLEMENTED;
174 }
175
176 /*
177 * @implemented
178 */
179 NTSTATUS
180 NTAPI
181 NtRequestWaitReplyPort(IN HANDLE PortHandle,
182 IN PPORT_MESSAGE LpcRequest,
183 IN OUT PPORT_MESSAGE LpcReply)
184 {
185 PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort;
186 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
187 NTSTATUS Status;
188 PLPCP_MESSAGE Message;
189 PETHREAD Thread = PsGetCurrentThread();
190 BOOLEAN Callback;
191 PKSEMAPHORE Semaphore;
192 ULONG MessageType;
193 PAGED_CODE();
194 LPCTRACE(LPC_SEND_DEBUG,
195 "Handle: %lx. Messages: %p/%p. Type: %lx\n",
196 PortHandle,
197 LpcRequest,
198 LpcReply,
199 LpcpGetMessageType(LpcRequest));
200
201 /* Check if the thread is dying */
202 if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
203
204 /* Check if this is an LPC Request */
205 if (LpcpGetMessageType(LpcRequest) == LPC_REQUEST)
206 {
207 /* Then it's a callback */
208 Callback = TRUE;
209 }
210 else if (LpcpGetMessageType(LpcRequest))
211 {
212 /* This is a not kernel-mode message */
213 return STATUS_INVALID_PARAMETER;
214 }
215 else
216 {
217 /* This is a kernel-mode message without a callback */
218 LpcRequest->u2.s2.Type |= LPC_REQUEST;
219 Callback = FALSE;
220 }
221
222 /* Get the message type */
223 MessageType = LpcRequest->u2.s2.Type;
224
225 /* Validate the length */
226 if ((LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
227 LpcRequest->u1.s1.TotalLength)
228 {
229 /* Fail */
230 return STATUS_INVALID_PARAMETER;
231 }
232
233 /* Reference the object */
234 Status = ObReferenceObjectByHandle(PortHandle,
235 0,
236 LpcPortObjectType,
237 PreviousMode,
238 (PVOID*)&Port,
239 NULL);
240 if (!NT_SUCCESS(Status)) return Status;
241
242 /* Validate the message length */
243 if ((LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) ||
244 (LpcRequest->u1.s1.TotalLength <= LpcRequest->u1.s1.DataLength))
245 {
246 /* Fail */
247 ObDereferenceObject(Port);
248 return STATUS_PORT_MESSAGE_TOO_LONG;
249 }
250
251 /* Allocate a message from the port zone */
252 Message = LpcpAllocateFromPortZone();
253 if (!Message)
254 {
255 /* Fail if we couldn't allocate a message */
256 ObDereferenceObject(Port);
257 return STATUS_NO_MEMORY;
258 }
259
260 /* Check if this is a callback */
261 if (Callback)
262 {
263 /* FIXME: TODO */
264 Semaphore = NULL; // we'd use the Thread Semaphore here
265 ASSERT(FALSE);
266 }
267 else
268 {
269 /* No callback, just copy the message */
270 LpcpMoveMessage(&Message->Request,
271 LpcRequest,
272 LpcRequest + 1,
273 MessageType,
274 &Thread->Cid);
275
276 /* Acquire the LPC lock */
277 KeAcquireGuardedMutex(&LpcpLock);
278
279 /* Right now clear the port context */
280 Message->PortContext = NULL;
281
282 /* Check if this is a not connection port */
283 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT)
284 {
285 /* We want the connected port */
286 QueuePort = Port->ConnectedPort;
287 if (!QueuePort)
288 {
289 /* We have no connected port, fail */
290 LpcpFreeToPortZone(Message, TRUE);
291 KeReleaseGuardedMutex(&LpcpLock);
292 ObDereferenceObject(Port);
293 return STATUS_PORT_DISCONNECTED;
294 }
295
296 /* This will be the rundown port */
297 ReplyPort = QueuePort;
298
299 /* Check if this is a communication port */
300 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
301 {
302 /* Copy the port context and use the connection port */
303 Message->PortContext = ReplyPort->PortContext;
304 QueuePort = Port->ConnectionPort;
305 }
306 else if ((Port->Flags & LPCP_PORT_TYPE_MASK) !=
307 LPCP_COMMUNICATION_PORT)
308 {
309 /* Use the connection port for anything but communication ports */
310 QueuePort = Port->ConnectionPort;
311 }
312 }
313 else
314 {
315 /* Otherwise, for a connection port, use the same port object */
316 QueuePort = ReplyPort = Port;
317 }
318
319 /* No reply thread */
320 Message->RepliedToThread = NULL;
321
322 /* Generate the Message ID and set it */
323 Message->Request.MessageId = LpcpNextMessageId++;
324 if (!LpcpNextMessageId) LpcpNextMessageId = 1;
325 Message->Request.CallbackId = 0;
326
327 /* Set the message ID for our thread now */
328 Thread->LpcReplyMessageId = Message->Request.MessageId;
329 Thread->LpcReplyMessage = NULL;
330
331 /* Insert the message in our chain */
332 InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
333 InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain);
334
335 /* Release the lock and get the semaphore we'll use later */
336 KeReleaseGuardedMutex(&LpcpLock);
337 Semaphore = QueuePort->MsgQueue.Semaphore;
338
339 /* If this is a waitable port, wake it up */
340 if (QueuePort->Flags & LPCP_WAITABLE_PORT)
341 {
342 /* Wake it */
343 KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE);
344 }
345 }
346
347 /* Now release the semaphore */
348 LpcpCompleteWait(Semaphore);
349
350 /* And let's wait for the reply */
351 LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
352
353 /* Acquire the LPC lock */
354 KeAcquireGuardedMutex(&LpcpLock);
355
356 /* Get the LPC Message and clear our thread's reply data */
357 Message = Thread->LpcReplyMessage;
358 Thread->LpcReplyMessage = NULL;
359 Thread->LpcReplyMessageId = 0;
360
361 /* Check if we have anything on the reply chain*/
362 if (!IsListEmpty(&Thread->LpcReplyChain))
363 {
364 /* Remove this thread and reinitialize the list */
365 RemoveEntryList(&Thread->LpcReplyChain);
366 InitializeListHead(&Thread->LpcReplyChain);
367 }
368
369 /* Release the lock */
370 KeReleaseGuardedMutex(&LpcpLock);
371
372 /* Check if we got a reply */
373 if (Status == STATUS_SUCCESS)
374 {
375 /* Check if we have a valid message */
376 if (Message)
377 {
378 LPCTRACE(LPC_SEND_DEBUG,
379 "Reply Messages: %p/%p\n",
380 &Message->Request,
381 (&Message->Request) + 1);
382
383 /* Move the message */
384 LpcpMoveMessage(LpcReply,
385 &Message->Request,
386 (&Message->Request) + 1,
387 0,
388 NULL);
389
390 /* Check if this is an LPC request with data information */
391 if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) &&
392 (Message->Request.u2.s2.DataInfoOffset))
393 {
394 /* Save the data information */
395 LpcpSaveDataInfoMessage(Port, Message);
396 }
397 else
398 {
399 /* Otherwise, just free it */
400 LpcpFreeToPortZone(Message, FALSE);
401 }
402 }
403 else
404 {
405 /* We don't have a reply */
406 Status = STATUS_LPC_REPLY_LOST;
407 }
408 }
409 else
410 {
411 /* The wait failed, free the message while holding the lock */
412 KeAcquireGuardedMutex(&LpcpLock);
413 LpcpFreeToPortZone(Message, TRUE);
414 KeReleaseGuardedMutex(&LpcpLock);
415 }
416
417 /* All done */
418 LPCTRACE(LPC_SEND_DEBUG,
419 "Port: %p. Status: %p\n",
420 Port,
421 Status);
422 ObDereferenceObject(Port);
423 return Status;
424 }
425
426 /* EOF */