update for HEAD-2003091401
[reactos.git] / ntoskrnl / lpc / reply.c
1 /* $Id$
2  * 
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            ntoskrnl/lpc/reply.c
6  * PURPOSE:         Communication mechanism
7  * PROGRAMMER:      David Welch (welch@cwcom.net)
8  * UPDATE HISTORY:
9  *                  Created 22/05/98
10  */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <internal/ob.h>
16 #include <internal/port.h>
17 #include <internal/dbg.h>
18 #include <internal/pool.h>
19 #include <internal/safe.h>
20
21 #define NDEBUG
22 #include <internal/debug.h>
23
24 /* GLOBALS *******************************************************************/
25
26 #define TAG_LPC_MESSAGE   TAG('L', 'P', 'C', 'M')
27
28 /* FUNCTIONS *****************************************************************/
29
30 /**********************************************************************
31  * NAME
32  *
33  * DESCRIPTION
34  *
35  * ARGUMENTS
36  *
37  * RETURN VALUE
38  *
39  * REVISIONS
40  */
41 NTSTATUS STDCALL
42 EiReplyOrRequestPort (IN        PEPORT          Port, 
43                       IN        PLPC_MESSAGE    LpcReply, 
44                       IN        ULONG           MessageType,
45                       IN        PEPORT          Sender)
46 {
47    KIRQL oldIrql;
48    PQUEUEDMESSAGE MessageReply;
49    
50    if (Port == NULL)
51      {
52        KEBUGCHECK(0);
53      }
54
55    MessageReply = ExAllocatePoolWithTag(NonPagedPool, sizeof(QUEUEDMESSAGE),
56                                         TAG_LPC_MESSAGE);
57    MessageReply->Sender = Sender;
58    
59    if (LpcReply != NULL)
60      {
61         memcpy(&MessageReply->Message, LpcReply, LpcReply->MessageSize);
62      }
63    
64    MessageReply->Message.ClientId.UniqueProcess = PsGetCurrentProcessId();
65    MessageReply->Message.ClientId.UniqueThread = PsGetCurrentThreadId();
66    MessageReply->Message.MessageType = MessageType;
67    MessageReply->Message.MessageId = InterlockedIncrement((LONG *)&EiNextLpcMessageId);
68    
69    KeAcquireSpinLock(&Port->Lock, &oldIrql);
70    EiEnqueueMessagePort(Port, MessageReply);
71    KeReleaseSpinLock(&Port->Lock, oldIrql);
72    
73    return(STATUS_SUCCESS);
74 }
75
76
77 /**********************************************************************
78  * NAME                                                 EXPORTED
79  *
80  * DESCRIPTION
81  *
82  * ARGUMENTS
83  *
84  * RETURN VALUE
85  *
86  * REVISIONS
87  */
88 NTSTATUS STDCALL
89 NtReplyPort (IN HANDLE          PortHandle,
90              IN PLPC_MESSAGE    LpcReply)
91 {
92    NTSTATUS Status;
93    PEPORT Port;
94    
95    DPRINT("NtReplyPort(PortHandle %x, LpcReply %x)\n", PortHandle, LpcReply);
96    
97    Status = ObReferenceObjectByHandle(PortHandle,
98                                       PORT_ALL_ACCESS,   /* AccessRequired */
99                                       ExPortType,
100                                       UserMode,
101                                       (PVOID*)&Port,
102                                       NULL);
103    if (!NT_SUCCESS(Status))
104      {
105         DPRINT("NtReplyPort() = %x\n", Status);
106         return(Status);
107      }
108    
109    Status = EiReplyOrRequestPort(Port->OtherPort, 
110                                  LpcReply, 
111                                  LPC_REPLY,
112                                  Port);
113    KeReleaseSemaphore(&Port->OtherPort->Semaphore, IO_NO_INCREMENT, 1, FALSE);
114    
115    ObDereferenceObject(Port);
116    
117    return(Status);
118 }
119
120
121 /**********************************************************************
122  * NAME                                                 EXPORTED
123  *      NtReplyWaitReceivePortEx
124  *      
125  * DESCRIPTION
126  *      Can be used with waitable ports.
127  *      Present only in w2k+.
128  *      
129  * ARGUMENTS
130  *      PortHandle
131  *      PortId
132  *      LpcReply
133  *      LpcMessage
134  *      Timeout
135  *
136  * RETURN VALUE
137  *
138  * REVISIONS
139  */
140 NTSTATUS STDCALL
141 NtReplyWaitReceivePortEx(IN  HANDLE             PortHandle,
142                          OUT PULONG             PortId,
143                          IN  PLPC_MESSAGE       LpcReply,     
144                          OUT PLPC_MESSAGE       LpcMessage,
145                          IN  PLARGE_INTEGER     Timeout)
146 {
147    NTSTATUS Status;
148    PEPORT Port;
149    KIRQL oldIrql;
150    PQUEUEDMESSAGE Request;
151    BOOLEAN Disconnected;
152    LARGE_INTEGER to;
153    
154    DPRINT("NtReplyWaitReceivePortEx(PortHandle %x, LpcReply %x, "
155           "LpcMessage %x)\n", PortHandle, LpcReply, LpcMessage);
156    
157    Status = ObReferenceObjectByHandle(PortHandle,
158                                       PORT_ALL_ACCESS,
159                                       ExPortType,
160                                       UserMode,
161                                       (PVOID*)&Port,
162                                       NULL);
163    if (!NT_SUCCESS(Status))
164      {
165         DPRINT1("NtReplyWaitReceivePortEx() = %x\n", Status);
166         return(Status);
167      }
168    if( Port->State == EPORT_DISCONNECTED )
169      {
170        /* If the port is disconnected, force the timeout to be 0
171         * so we don't wait for new messages, because there won't be
172         * any, only try to remove any existing messages
173         */
174        Disconnected = TRUE;
175        to.QuadPart = 0;
176        Timeout = &to;
177      }
178    else Disconnected = FALSE;
179    
180    /*
181     * Send the reply, only if port is connected
182     */
183    if (LpcReply != NULL && !Disconnected)
184      {
185         Status = EiReplyOrRequestPort(Port->OtherPort, 
186                                       LpcReply,
187                                       LPC_REPLY,
188                                       Port);
189         KeReleaseSemaphore(&Port->OtherPort->Semaphore, IO_NO_INCREMENT, 1, 
190                            FALSE);
191         
192         if (!NT_SUCCESS(Status))
193           {
194              ObDereferenceObject(Port);
195              DPRINT1("NtReplyWaitReceivePortEx() = %x\n", Status);
196              return(Status);
197           }
198      }
199    
200    /*
201     * Want for a message to be received
202     */
203    Status = KeWaitForSingleObject(&Port->Semaphore,
204                                   UserRequest,
205                                   UserMode,
206                                   FALSE,
207                                   Timeout);
208    if( Status == STATUS_TIMEOUT )
209      {
210        /*
211         * if the port is disconnected, and there are no remaining messages,
212         * return STATUS_PORT_DISCONNECTED
213         */
214        ObDereferenceObject(Port);
215        return(Disconnected ? STATUS_PORT_DISCONNECTED : STATUS_TIMEOUT);
216      }
217    
218    if (!NT_SUCCESS(Status))
219      {
220        if (STATUS_THREAD_IS_TERMINATING != Status)
221          {
222            DPRINT1("NtReplyWaitReceivePortEx() = %x\n", Status);
223          }
224        ObDereferenceObject(Port);
225        return(Status);
226      }
227
228    /*
229     * Dequeue the message
230     */
231    KeAcquireSpinLock(&Port->Lock, &oldIrql);
232    Request = EiDequeueMessagePort(Port);
233    KeReleaseSpinLock(&Port->Lock, oldIrql);
234
235    if (Request->Message.MessageType == LPC_CONNECTION_REQUEST)
236      {
237        LPC_MESSAGE Header;
238        PEPORT_CONNECT_REQUEST_MESSAGE CRequest;
239
240        CRequest = (PEPORT_CONNECT_REQUEST_MESSAGE)&Request->Message;
241        memcpy(&Header, &Request->Message, sizeof(LPC_MESSAGE));
242        Header.DataSize = CRequest->ConnectDataLength;
243        Header.MessageSize = Header.DataSize + sizeof(LPC_MESSAGE);
244        Status = MmCopyToCaller(LpcMessage, &Header, sizeof(LPC_MESSAGE));
245        if (NT_SUCCESS(Status))
246          {
247            Status = MmCopyToCaller((PVOID)(LpcMessage + 1),
248                                    CRequest->ConnectData,
249                                    CRequest->ConnectDataLength);
250          }
251      }
252    else
253      {
254        Status = MmCopyToCaller(LpcMessage, &Request->Message,
255                                Request->Message.MessageSize);
256      }
257    if (!NT_SUCCESS(Status))
258      {
259        /*
260         * Copying the message to the caller's buffer failed so 
261         * undo what we did and return.
262         * FIXME: Also increment semaphore.
263         */
264        KeAcquireSpinLock(&Port->Lock, &oldIrql);
265        EiEnqueueMessageAtHeadPort(Port, Request);
266        KeReleaseSpinLock(&Port->Lock, oldIrql);
267        ObDereferenceObject(Port);
268        return(Status);
269      }
270    if (Request->Message.MessageType == LPC_CONNECTION_REQUEST)
271      {
272        KeAcquireSpinLock(&Port->Lock, &oldIrql);
273        EiEnqueueConnectMessagePort(Port, Request);
274        KeReleaseSpinLock(&Port->Lock, oldIrql);
275      }
276    else
277      {
278        ExFreePool(Request);
279      }
280    
281    /*
282     * Dereference the port
283     */
284    ObDereferenceObject(Port);
285    return(STATUS_SUCCESS);
286 }
287
288
289 /**********************************************************************
290  * NAME                                         EXPORTED
291  *      NtReplyWaitReceivePort
292  *      
293  * DESCRIPTION
294  *      Can be used with waitable ports.
295  *      
296  * ARGUMENTS
297  *      PortHandle
298  *      PortId
299  *      LpcReply
300  *      LpcMessage
301  *
302  * RETURN VALUE
303  *
304  * REVISIONS
305  */
306 NTSTATUS STDCALL
307 NtReplyWaitReceivePort (IN  HANDLE              PortHandle,
308                         OUT PULONG              PortId,
309                         IN  PLPC_MESSAGE        LpcReply,     
310                         OUT PLPC_MESSAGE        LpcMessage)
311 {
312   return(NtReplyWaitReceivePortEx (PortHandle,
313                                    PortId,
314                                    LpcReply,
315                                    LpcMessage,
316                                    NULL));
317 }
318
319 /**********************************************************************
320  * NAME
321  *
322  * DESCRIPTION
323  *
324  * ARGUMENTS
325  *
326  * RETURN VALUE
327  *
328  * REVISIONS
329  */
330 NTSTATUS STDCALL
331 NtReplyWaitReplyPort (HANDLE            PortHandle,
332                       PLPC_MESSAGE      ReplyMessage)
333 {
334    UNIMPLEMENTED;
335    return(STATUS_NOT_IMPLEMENTED);
336 }
337
338
339 /* EOF */