[NTDLL]
[reactos.git] / subsystems / win32 / csrsrv / session.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS CSR SubSystem
4 * FILE: subsystems/win32/csrss/csrsrv/session.c
5 * PURPOSE: CSR Server DLL Session Implementation
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "srv.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* DATA **********************************************************************/
17
18 RTL_CRITICAL_SECTION CsrNtSessionLock;
19 LIST_ENTRY CsrNtSessionList;
20
21 // Does it exist a enumeration associated with it ?
22 PSB_API_ROUTINE CsrServerSbApiDispatch[5] =
23 {
24 CsrSbCreateSession,
25 CsrSbTerminateSession,
26 CsrSbForeignSessionComplete,
27 CsrSbCreateProcess,
28 NULL
29 };
30
31 PCHAR CsrServerSbApiName[5] =
32 {
33 "SbCreateSession",
34 "SbTerminateSession",
35 "SbForeignSessionComplete",
36 "SbCreateProcess",
37 "Unknown Csr Sb Api Number"
38 };
39
40 /* PRIVATE FUNCTIONS *********************************************************/
41
42 /*++
43 * @name CsrInitializeNtSessionList
44 *
45 * The CsrInitializeNtSessionList routine sets up support for CSR Sessions.
46 *
47 * @param None
48 *
49 * @return None
50 *
51 * @remarks None.
52 *
53 *--*/
54 NTSTATUS
55 NTAPI
56 CsrInitializeNtSessionList(VOID)
57 {
58 DPRINT("CSRSRV: %s called\n", __FUNCTION__);
59
60 /* Initialize the Session List */
61 InitializeListHead(&CsrNtSessionList);
62
63 /* Initialize the Session Lock */
64 return RtlInitializeCriticalSection(&CsrNtSessionLock);
65 }
66
67 /*++
68 * @name CsrAllocateNtSession
69 *
70 * The CsrAllocateNtSession routine allocates a new CSR NT Session.
71 *
72 * @param SessionId
73 * Session ID of the CSR NT Session to allocate.
74 *
75 * @return Pointer to the newly allocated CSR NT Session.
76 *
77 * @remarks None.
78 *
79 *--*/
80 PCSR_NT_SESSION
81 NTAPI
82 CsrAllocateNtSession(IN ULONG SessionId)
83 {
84 PCSR_NT_SESSION NtSession;
85
86 /* Allocate an NT Session Object */
87 NtSession = RtlAllocateHeap(CsrHeap, 0, sizeof(CSR_NT_SESSION));
88 if (NtSession)
89 {
90 /* Setup the Session Object */
91 NtSession->SessionId = SessionId;
92 NtSession->ReferenceCount = 1;
93
94 /* Insert it into the Session List */
95 CsrAcquireNtSessionLock();
96 InsertHeadList(&CsrNtSessionList, &NtSession->SessionLink);
97 CsrReleaseNtSessionLock();
98 }
99 else
100 {
101 ASSERT(NtSession != NULL);
102 }
103
104 /* Return the Session (or NULL) */
105 return NtSession;
106 }
107
108 /*++
109 * @name CsrReferenceNtSession
110 *
111 * The CsrReferenceNtSession increases the reference count of a CSR NT Session.
112 *
113 * @param Session
114 * Pointer to the CSR NT Session to reference.
115 *
116 * @return None.
117 *
118 * @remarks None.
119 *
120 *--*/
121 VOID
122 NTAPI
123 CsrReferenceNtSession(IN PCSR_NT_SESSION Session)
124 {
125 /* Acquire the lock */
126 CsrAcquireNtSessionLock();
127
128 /* Sanity checks */
129 ASSERT(!IsListEmpty(&Session->SessionLink));
130 ASSERT(Session->SessionId != 0);
131 ASSERT(Session->ReferenceCount != 0);
132
133 /* Increase the reference count */
134 Session->ReferenceCount++;
135
136 /* Release the lock */
137 CsrReleaseNtSessionLock();
138 }
139
140 /*++
141 * @name CsrDereferenceNtSession
142 *
143 * The CsrDereferenceNtSession decreases the reference count of a
144 * CSR NT Session.
145 *
146 * @param Session
147 * Pointer to the CSR NT Session to reference.
148 *
149 * @param ExitStatus
150 * If this is the last reference to the session, this argument
151 * specifies the exit status.
152 *
153 * @return None.
154 *
155 * @remarks CsrDereferenceNtSession will complete the session if
156 * the last reference to it has been closed.
157 *
158 *--*/
159 VOID
160 NTAPI
161 CsrDereferenceNtSession(IN PCSR_NT_SESSION Session,
162 IN NTSTATUS ExitStatus)
163 {
164 /* Acquire the lock */
165 CsrAcquireNtSessionLock();
166
167 /* Sanity checks */
168 ASSERT(!IsListEmpty(&Session->SessionLink));
169 ASSERT(Session->SessionId != 0);
170 ASSERT(Session->ReferenceCount != 0);
171
172 /* Dereference the Session Object */
173 if (!(--Session->ReferenceCount))
174 {
175 /* Remove it from the list */
176 RemoveEntryList(&Session->SessionLink);
177
178 /* Release the lock */
179 CsrReleaseNtSessionLock();
180
181 /* Tell SM that we're done here */
182 SmSessionComplete(CsrSmApiPort, Session->SessionId, ExitStatus);
183
184 /* Free the Session Object */
185 RtlFreeHeap(CsrHeap, 0, Session);
186 }
187 else
188 {
189 /* Release the lock, the Session is still active */
190 CsrReleaseNtSessionLock();
191 }
192 }
193
194 /* SESSION MANAGER FUNCTIONS**************************************************/
195
196 /*++
197 * @name CsrSbCreateSession
198 *
199 * The CsrSbCreateSession API is called by the Session Manager whenever a new
200 * session is created.
201 *
202 * @param ApiMessage
203 * Pointer to the Session Manager API Message.
204 *
205 * @return TRUE in case of success, FALSE otherwise.
206 *
207 * @remarks The CsrSbCreateSession routine will initialize a new CSR NT
208 * Session and allocate a new CSR Process for the subsystem process.
209 *
210 *--*/
211 BOOLEAN
212 NTAPI
213 CsrSbCreateSession(IN PSB_API_MSG ApiMessage)
214 {
215 PSB_CREATE_SESSION_MSG CreateSession = &ApiMessage->CreateSession;
216 HANDLE hProcess, hThread;
217 PCSR_PROCESS CsrProcess;
218 NTSTATUS Status;
219 KERNEL_USER_TIMES KernelTimes;
220 PCSR_THREAD CsrThread;
221 PVOID ProcessData;
222 ULONG i;
223
224 /* Save the Process and Thread Handles */
225 hProcess = CreateSession->ProcessInfo.ProcessHandle;
226 hThread = CreateSession->ProcessInfo.ThreadHandle;
227
228 /* Lock the Processes */
229 CsrAcquireProcessLock();
230
231 /* Allocate a new process */
232 CsrProcess = CsrAllocateProcess();
233 if (!CsrProcess)
234 {
235 /* Fail */
236 ApiMessage->ReturnValue = STATUS_NO_MEMORY;
237 CsrReleaseProcessLock();
238 return TRUE;
239 }
240
241 /* Set the exception port */
242 Status = NtSetInformationProcess(hProcess,
243 ProcessExceptionPort,
244 &CsrApiPort,
245 sizeof(HANDLE));
246
247 /* Check for success */
248 if (!NT_SUCCESS(Status))
249 {
250 /* Fail the request */
251 CsrDeallocateProcess(CsrProcess);
252 CsrReleaseProcessLock();
253
254 /* Strange as it seems, NTSTATUSes are actually returned */
255 return (BOOLEAN)STATUS_NO_MEMORY;
256 }
257
258 /* Get the Create Time */
259 Status = NtQueryInformationThread(hThread,
260 ThreadTimes,
261 &KernelTimes,
262 sizeof(KERNEL_USER_TIMES),
263 NULL);
264
265 /* Check for success */
266 if (!NT_SUCCESS(Status))
267 {
268 /* Fail the request */
269 CsrDeallocateProcess(CsrProcess);
270 CsrReleaseProcessLock();
271
272 /* Strange as it seems, NTSTATUSes are actually returned */
273 return (BOOLEAN)Status;
274 }
275
276 /* Allocate a new Thread */
277 CsrThread = CsrAllocateThread(CsrProcess);
278 if (!CsrThread)
279 {
280 /* Fail the request */
281 CsrDeallocateProcess(CsrProcess);
282 CsrReleaseProcessLock();
283
284 ApiMessage->ReturnValue = STATUS_NO_MEMORY;
285 return TRUE;
286 }
287
288 /* Setup the Thread Object */
289 CsrThread->CreateTime = KernelTimes.CreateTime;
290 CsrThread->ClientId = CreateSession->ProcessInfo.ClientId;
291 CsrThread->ThreadHandle = hThread;
292 ProtectHandle(hThread);
293 CsrThread->Flags = 0;
294
295 /* Insert it into the Process List */
296 CsrInsertThread(CsrProcess, CsrThread);
297
298 /* Setup Process Data */
299 CsrProcess->ClientId = CreateSession->ProcessInfo.ClientId;
300 CsrProcess->ProcessHandle = hProcess;
301 CsrProcess->NtSession = CsrAllocateNtSession(CreateSession->SessionId);
302
303 /* Set the Process Priority */
304 CsrSetBackgroundPriority(CsrProcess);
305
306 /* Get the first data location */
307 ProcessData = &CsrProcess->ServerData[CSR_SERVER_DLL_MAX];
308
309 /* Loop every DLL */
310 for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
311 {
312 /* Check if the DLL is loaded and has Process Data */
313 if (CsrLoadedServerDll[i] && CsrLoadedServerDll[i]->SizeOfProcessData)
314 {
315 /* Write the pointer to the data */
316 CsrProcess->ServerData[i] = ProcessData;
317
318 /* Move to the next data location */
319 ProcessData = (PVOID)((ULONG_PTR)ProcessData +
320 CsrLoadedServerDll[i]->SizeOfProcessData);
321 }
322 else
323 {
324 /* Nothing for this Process */
325 CsrProcess->ServerData[i] = NULL;
326 }
327 }
328
329 /* HACK: FIXME: should go in BaseSrv part of CreateCallback done in Insert below */
330 // RtlInitializeCriticalSection(&CsrProcess->HandleTableLock);
331
332 /* Insert the Process */
333 CsrInsertProcess(NULL, NULL, CsrProcess);
334
335 /* Activate the Thread */
336 ApiMessage->ReturnValue = NtResumeThread(hThread, NULL);
337
338 /* Release lock and return */
339 CsrReleaseProcessLock();
340 return TRUE;
341 }
342
343 /*++
344 * @name CsrSbForeignSessionComplete
345 *
346 * The CsrSbForeignSessionComplete API is called by the Session Manager
347 * whenever a foreign session is completed (ie: terminated).
348 *
349 * @param ApiMessage
350 * Pointer to the Session Manager API Message.
351 *
352 * @return TRUE in case of success, FALSE otherwise.
353 *
354 * @remarks The CsrSbForeignSessionComplete API is not yet implemented.
355 *
356 *--*/
357 BOOLEAN
358 NTAPI
359 CsrSbForeignSessionComplete(IN PSB_API_MSG ApiMessage)
360 {
361 /* Deprecated/Unimplemented in NT */
362 ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED;
363 return TRUE;
364 }
365
366 /*++
367 * @name CsrSbTerminateSession
368 *
369 * The CsrSbTerminateSession API is called by the Session Manager
370 * whenever a foreign session should be destroyed.
371 *
372 * @param ApiMessage
373 * Pointer to the Session Manager API Message.
374 *
375 * @return TRUE in case of success, FALSE otherwise.
376 *
377 * @remarks The CsrSbTerminateSession API is not yet implemented.
378 *
379 *--*/
380 BOOLEAN
381 NTAPI
382 CsrSbTerminateSession(IN PSB_API_MSG ApiMessage)
383 {
384 ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED;
385 return TRUE;
386 }
387
388 /*++
389 * @name CsrSbCreateProcess
390 *
391 * The CsrSbCreateProcess API is called by the Session Manager
392 * whenever a foreign session is created and a new process should be started.
393 *
394 * @param ApiMessage
395 * Pointer to the Session Manager API Message.
396 *
397 * @return TRUE in case of success, FALSE otherwise.
398 *
399 * @remarks The CsrSbCreateProcess API is not yet implemented.
400 *
401 *--*/
402 BOOLEAN
403 NTAPI
404 CsrSbCreateProcess(IN PSB_API_MSG ApiMessage)
405 {
406 ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED;
407 return TRUE;
408 }
409
410 /*++
411 * @name CsrSbApiHandleConnectionRequest
412 *
413 * The CsrSbApiHandleConnectionRequest routine handles and accepts a new
414 * connection request to the SM API LPC Port.
415 *
416 * @param ApiMessage
417 * Pointer to the incoming CSR API Message which contains the
418 * connection request.
419 *
420 * @return STATUS_SUCCESS in case of success, or status code which caused
421 * the routine to error.
422 *
423 * @remarks None.
424 *
425 *--*/
426 NTSTATUS
427 NTAPI
428 CsrSbApiHandleConnectionRequest(IN PSB_API_MSG Message)
429 {
430 NTSTATUS Status;
431 REMOTE_PORT_VIEW RemotePortView;
432 HANDLE hPort;
433
434 /* Set the Port View Structure Length */
435 RemotePortView.Length = sizeof(REMOTE_PORT_VIEW);
436
437 /* Accept the connection */
438 Status = NtAcceptConnectPort(&hPort,
439 NULL,
440 (PPORT_MESSAGE)Message,
441 TRUE,
442 NULL,
443 &RemotePortView);
444 if (!NT_SUCCESS(Status))
445 {
446 DPRINT1("CSRSS: Sb Accept Connection failed %lx\n", Status);
447 return Status;
448 }
449
450 /* Complete the Connection */
451 Status = NtCompleteConnectPort(hPort);
452 if (!NT_SUCCESS(Status))
453 {
454 DPRINT1("CSRSS: Sb Complete Connection failed %lx\n",Status);
455 }
456
457 /* Return status */
458 return Status;
459 }
460
461 /*++
462 * @name CsrSbApiRequestThread
463 *
464 * The CsrSbApiRequestThread routine handles incoming messages or connection
465 * requests on the SM API LPC Port.
466 *
467 * @param Parameter
468 * System-default user-defined parameter. Unused.
469 *
470 * @return The thread exit code, if the thread is terminated.
471 *
472 * @remarks Before listening on the port, the routine will first attempt
473 * to connect to the user subsystem.
474 *
475 *--*/
476 VOID
477 NTAPI
478 CsrSbApiRequestThread(IN PVOID Parameter)
479 {
480 NTSTATUS Status;
481 SB_API_MSG ReceiveMsg;
482 PSB_API_MSG ReplyMsg = NULL;
483 PVOID PortContext;
484 ULONG MessageType;
485
486 /* Start the loop */
487 while (TRUE)
488 {
489 /* Wait for a message to come in */
490 Status = NtReplyWaitReceivePort(CsrSbApiPort,
491 &PortContext,
492 &ReplyMsg->h,
493 &ReceiveMsg.h);
494
495 /* Check if we didn't get success */
496 if (Status != STATUS_SUCCESS)
497 {
498 /* If we only got a warning, keep going */
499 if (NT_SUCCESS(Status)) continue;
500
501 /* We failed big time, so start out fresh */
502 ReplyMsg = NULL;
503 DPRINT1("CSRSS: ReceivePort failed - Status == %X\n", Status);
504 continue;
505 }
506
507 /* Save the message type */
508 MessageType = ReceiveMsg.h.u2.s2.Type;
509
510 /* Check if this is a connection request */
511 if (MessageType == LPC_CONNECTION_REQUEST)
512 {
513 /* Handle connection request */
514 CsrSbApiHandleConnectionRequest(&ReceiveMsg);
515
516 /* Start over */
517 ReplyMsg = NULL;
518 continue;
519 }
520
521 /* Check if the port died */
522 if (MessageType == LPC_PORT_CLOSED)
523 {
524 /* Close the handle if we have one */
525 if (PortContext) NtClose((HANDLE)PortContext);
526
527 /* Client died, start over */
528 ReplyMsg = NULL;
529 continue;
530 }
531 else if (MessageType == LPC_CLIENT_DIED)
532 {
533 /* Client died, start over */
534 ReplyMsg = NULL;
535 continue;
536 }
537
538 /*
539 * It's an API Message, check if it's within limits. If it's not, the
540 * NT Behaviour is to set this to the Maximum API.
541 */
542 if (ReceiveMsg.ApiNumber > SbpMaxApiNumber)
543 {
544 ReceiveMsg.ApiNumber = SbpMaxApiNumber;
545 DPRINT1("CSRSS: %lx is invalid Sb ApiNumber\n", ReceiveMsg.ApiNumber);
546 }
547
548 /* Reuse the message */
549 ReplyMsg = &ReceiveMsg;
550
551 /* Make sure that the message is supported */
552 if (ReceiveMsg.ApiNumber < SbpMaxApiNumber)
553 {
554 /* Call the API */
555 if (!CsrServerSbApiDispatch[ReceiveMsg.ApiNumber](&ReceiveMsg))
556 {
557 /* It failed, so return nothing */
558 ReplyMsg = NULL;
559 }
560 }
561 else
562 {
563 /* We don't support this API Number */
564 ReplyMsg->ReturnValue = STATUS_NOT_IMPLEMENTED;
565 }
566 }
567 }
568
569 /* EOF */