bf42824a203b78eadb2db45cf15239a9daa6abc1
[reactos.git] / reactos / ntoskrnl / lpc / close.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/close.c
5 * PURPOSE: Local Procedure Call: Rundown, Cleanup, Deletion
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PRIVATE FUNCTIONS *********************************************************/
16
17 VOID
18 NTAPI
19 LpcExitThread(IN PETHREAD Thread)
20 {
21 PLPCP_MESSAGE Message;
22
23 /* Acquire the lock */
24 KeAcquireGuardedMutex(&LpcpLock);
25
26 /* Make sure that the Reply Chain is empty */
27 if (!IsListEmpty(&Thread->LpcReplyChain))
28 {
29 /* It's not, remove the entry */
30 RemoveEntryList(&Thread->LpcReplyChain);
31 }
32
33 /* Set the thread in exit mode */
34 Thread->LpcExitThreadCalled = TRUE;
35 Thread->LpcReplyMessageId = 0;
36
37 /* Check if there's a reply message */
38 Message = Thread->LpcReplyMessage;
39 if (Message)
40 {
41 /* FIXME: TODO */
42 KEBUGCHECK(0);
43 }
44
45 /* Release the lock */
46 KeReleaseGuardedMutex(&LpcpLock);
47 }
48
49 VOID
50 NTAPI
51 LpcpFreeToPortZone(IN PLPCP_MESSAGE Message,
52 IN ULONG Flags)
53 {
54 PLPCP_CONNECTION_MESSAGE ConnectMessage;
55 PLPCP_PORT_OBJECT ClientPort = NULL;
56 PETHREAD Thread = NULL;
57 BOOLEAN LockHeld = Flags & 1;
58 PAGED_CODE();
59 LPCTRACE(LPC_CLOSE_DEBUG, "Message: %p. Flags: %lx\n", Message, Flags);
60
61 /* Acquire the lock if not already */
62 if (!LockHeld) KeAcquireGuardedMutex(&LpcpLock);
63
64 /* Check if the queue list is empty */
65 if (!IsListEmpty(&Message->Entry))
66 {
67 /* Remove and re-initialize */
68 RemoveEntryList(&Message->Entry);
69 InitializeListHead(&Message->Entry);
70 }
71
72 /* Check if we've already replied */
73 if (Message->RepliedToThread)
74 {
75 /* Set thread to dereference and clean up */
76 Thread = Message->RepliedToThread;
77 Message->RepliedToThread = NULL;
78 }
79
80 /* Check if this is a connection request */
81 if (Message->Request.u2.s2.Type == LPC_CONNECTION_REQUEST)
82 {
83 /* Get the connection message */
84 ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
85
86 /* Clear the client port */
87 ClientPort = ConnectMessage->ClientPort;
88 if (ClientPort) ConnectMessage->ClientPort = NULL;
89 }
90
91 /* Release the lock */
92 KeReleaseGuardedMutex(&LpcpLock);
93
94 /* Check if we had anything to dereference */
95 if (Thread) ObDereferenceObject(Thread);
96 if (ClientPort) ObDereferenceObject(ClientPort);
97
98 /* Free the entry */
99 ExFreeToPagedLookasideList(&LpcpMessagesLookaside, Message);
100
101 /* Reacquire the lock if needed */
102 if ((LockHeld) && !(Flags & 2)) KeAcquireGuardedMutex(&LpcpLock);
103 }
104
105 VOID
106 NTAPI
107 LpcpDestroyPortQueue(IN PLPCP_PORT_OBJECT Port,
108 IN BOOLEAN Destroy)
109 {
110 PLIST_ENTRY ListHead, NextEntry;
111 PETHREAD Thread;
112 PLPCP_MESSAGE Message;
113 PLPCP_CONNECTION_MESSAGE ConnectMessage;
114 LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags);
115
116 /* Hold the lock */
117 KeAcquireGuardedMutex(&LpcpLock);
118
119 /* Disconnect the port to which this port is connected */
120 if (Port->ConnectedPort) Port->ConnectedPort->ConnectedPort = NULL;
121
122 /* Check if this is a connection port */
123 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT)
124 {
125 /* Delete the name */
126 Port->Flags |= LPCP_NAME_DELETED;
127 }
128
129 /* Walk all the threads waiting and signal them */
130 ListHead = &Port->LpcReplyChainHead;
131 NextEntry = ListHead->Flink;
132 while (NextEntry != ListHead)
133 {
134 /* Get the Thread */
135 Thread = CONTAINING_RECORD(NextEntry, ETHREAD, LpcReplyChain);
136
137 /* Make sure we're not in exit */
138 if (Thread->LpcExitThreadCalled) break;
139
140 /* Move to the next entry */
141 NextEntry = NextEntry->Flink;
142
143 /* Remove and reinitialize the List */
144 RemoveEntryList(&Thread->LpcReplyChain);
145 InitializeListHead(&Thread->LpcReplyChain);
146
147 /* Check if someone is waiting */
148 if (!KeReadStateSemaphore(&Thread->LpcReplySemaphore))
149 {
150 /* Get the message and check if it's a connection request */
151 Message = Thread->LpcReplyMessage;
152 if (Message->Request.u2.s2.Type == LPC_CONNECTION_REQUEST)
153 {
154 /* Get the connection message */
155 ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
156
157 /* Check if it had a section */
158 if (ConnectMessage->SectionToMap)
159 {
160 /* Dereference it */
161 ObDereferenceObject(ConnectMessage->SectionToMap);
162 }
163 }
164
165 /* Clear the reply message */
166 Thread->LpcReplyMessage = NULL;
167
168 /* And remove the message from the port zone */
169 LpcpFreeToPortZone(Message, TRUE);
170 }
171
172 /* Release the semaphore and reset message id count */
173 Thread->LpcReplyMessageId = 0;
174 LpcpCompleteWait(&Thread->LpcReplySemaphore);
175 }
176
177 /* Reinitialize the list head */
178 InitializeListHead(&Port->LpcReplyChainHead);
179
180 /* Loop queued messages */
181 ListHead = &Port->MsgQueue.ReceiveHead;
182 NextEntry = ListHead->Flink;
183 while ((NextEntry) && (ListHead != NextEntry))
184 {
185 /* Get the message */
186 Message = CONTAINING_RECORD(NextEntry, LPCP_MESSAGE, Entry);
187 NextEntry = NextEntry->Flink;
188
189 /* Free and reinitialize it's list head */
190 InitializeListHead(&Message->Entry);
191
192 /* Remove it from the port zone */
193 LpcpFreeToPortZone(Message, TRUE);
194 }
195
196 /* Reinitialize the message queue list head */
197 InitializeListHead(&Port->MsgQueue.ReceiveHead);
198
199 /* Release the lock */
200 KeReleaseGuardedMutex(&LpcpLock);
201
202 /* Check if we have to free the port entirely */
203 if (Destroy)
204 {
205 /* Check if the semaphore exists */
206 if (Port->MsgQueue.Semaphore)
207 {
208 /* Use the semaphore to find the port queue and free it */
209 ExFreePool(CONTAINING_RECORD(Port->MsgQueue.Semaphore,
210 LPCP_NONPAGED_PORT_QUEUE,
211 Semaphore));
212 }
213 }
214 }
215
216 VOID
217 NTAPI
218 LpcpClosePort(IN PEPROCESS Process OPTIONAL,
219 IN PVOID Object,
220 IN ACCESS_MASK GrantedAccess,
221 IN ULONG ProcessHandleCount,
222 IN ULONG SystemHandleCount)
223 {
224 PLPCP_PORT_OBJECT Port = (PLPCP_PORT_OBJECT)Object;
225 LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags);
226
227 /* Only Server-side Connection Ports need clean up*/
228 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT)
229 {
230 /* Check the handle count */
231 switch (SystemHandleCount)
232 {
233 /* No handles left */
234 case 0:
235
236 /* Destroy the port queue */
237 LpcpDestroyPortQueue(Port, TRUE);
238 break;
239
240 /* Last handle remaining */
241 case 1:
242
243 /* Reset the queue only */
244 LpcpDestroyPortQueue(Port, FALSE);
245
246 /* More handles remain, do nothing */
247 default:
248 break;
249 }
250 }
251 }
252
253 VOID
254 NTAPI
255 LpcpFreePortClientSecurity(IN PLPCP_PORT_OBJECT Port)
256 {
257 /* Check if this is a client port */
258 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
259 {
260 /* Check if security is static */
261 if (!(Port->Flags & LPCP_SECURITY_DYNAMIC))
262 {
263 /* Check if we have a token */
264 if (Port->StaticSecurity.ClientToken)
265 {
266 /* Free security */
267 SeDeleteClientSecurity(&Port->StaticSecurity);
268 }
269 }
270 }
271 }
272
273 VOID
274 NTAPI
275 LpcpDeletePort(IN PVOID ObjectBody)
276 {
277 LARGE_INTEGER Timeout;
278 PETHREAD Thread;
279 PLPCP_PORT_OBJECT Port = (PLPCP_PORT_OBJECT)ObjectBody;
280 PLPCP_PORT_OBJECT ConnectionPort;
281 PLPCP_MESSAGE Message;
282 PLIST_ENTRY ListHead, NextEntry;
283 HANDLE Pid;
284 CLIENT_DIED_MSG ClientDiedMsg;
285 Timeout.QuadPart = -1000000;
286 PAGED_CODE();
287 LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags);
288
289 /* Check if this is a communication port */
290 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_COMMUNICATION_PORT)
291 {
292 /* Acquire the lock */
293 KeAcquireGuardedMutex(&LpcpLock);
294
295 /* Get the thread */
296 Thread = Port->ClientThread;
297 if (Thread)
298 {
299 /* Clear it */
300 Port->ClientThread = NULL;
301
302 /* Release the lock and dereference */
303 KeReleaseGuardedMutex(&LpcpLock);
304 ObDereferenceObject(Thread);
305 }
306 else
307 {
308 /* Release the lock */
309 KeReleaseGuardedMutex(&LpcpLock);
310 }
311 }
312
313 /* Check if this is a client-side port */
314 if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
315 {
316 /* Setup the client died message */
317 ClientDiedMsg.h.u1.s1.TotalLength = sizeof(ClientDiedMsg);
318 ClientDiedMsg.h.u1.s1.DataLength = sizeof(ClientDiedMsg.CreateTime);
319 ClientDiedMsg.h.u2.ZeroInit = LPC_PORT_CLOSED;
320 ClientDiedMsg.CreateTime = PsGetCurrentProcess()->CreateTime;
321
322 /* Send it */
323 for (;;)
324 {
325 /* Send the message */
326 if (LpcRequestPort(Port,
327 &ClientDiedMsg.h) != STATUS_NO_MEMORY) break;
328
329 /* Wait until trying again */
330 KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
331 }
332 }
333
334 /* Destroy the port queue */
335 LpcpDestroyPortQueue(Port, TRUE);
336
337 /* Check if we had a client view */
338 if (Port->ClientSectionBase) MmUnmapViewOfSection(PsGetCurrentProcess(),
339 Port->ClientSectionBase);
340
341 /* Check for a server view */
342 if (Port->ServerSectionBase) MmUnmapViewOfSection(PsGetCurrentProcess(),
343 Port->ServerSectionBase);
344
345 /* Get the connection port */
346 ConnectionPort = Port->ConnectionPort;
347 if (ConnectionPort)
348 {
349 /* Get the PID */
350 Pid = PsGetCurrentProcessId();
351
352 /* Acquire the lock */
353 KeAcquireGuardedMutex(&LpcpLock);
354
355 /* Loop the data lists */
356 ListHead = &ConnectionPort->LpcDataInfoChainHead;
357 NextEntry = ListHead->Flink;
358 while (NextEntry != ListHead)
359 {
360 /* Get the message */
361 Message = CONTAINING_RECORD(NextEntry, LPCP_MESSAGE, Entry);
362 NextEntry = NextEntry->Flink;
363
364 /* Check if the PID matches */
365 if (Message->Request.ClientId.UniqueProcess == Pid)
366 {
367 /* Remove it */
368 RemoveEntryList(&Message->Entry);
369 LpcpFreeToPortZone(Message, TRUE);
370 }
371 }
372
373 /* Release the lock */
374 KeReleaseGuardedMutex(&LpcpLock);
375
376 /* Dereference the object unless it's the same port */
377 if (ConnectionPort != Port) ObDereferenceObject(ConnectionPort);
378 }
379
380 /* Check if this is a connection port with a server process*/
381 if (((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT) &&
382 (ConnectionPort->ServerProcess))
383 {
384 /* Dereference the server process */
385 ObDereferenceObject(ConnectionPort->ServerProcess);
386 ConnectionPort->ServerProcess = NULL;
387 }
388
389 /* Free client security */
390 LpcpFreePortClientSecurity(Port);
391 LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p deleted\n", Port);
392 }
393
394 /* EOF */