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