[CSRSRV]
[reactos.git] / 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 CsrInsertThread(CsrProcess, CsrThread);
293
294 /* Setup Process Data */
295 CsrProcess->ClientId = CreateSession->ProcessInfo.ClientId;
296 CsrProcess->ProcessHandle = hProcess;
297 CsrProcess->NtSession = CsrAllocateNtSession(CreateSession->SessionId);
298
299 /* Set the Process Priority */
300 CsrSetBackgroundPriority(CsrProcess);
301
302 /* Get the first data location */
303 ProcessData = &CsrProcess->ServerData[CSR_SERVER_DLL_MAX];
304
305 /* Loop every DLL */
306 for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
307 {
308 /* Get the current Server */
309 ServerDll = CsrLoadedServerDll[i];
310
311 /* Check if the DLL is loaded and has Process Data */
312 if (ServerDll && ServerDll->SizeOfProcessData)
313 {
314 /* Write the pointer to the data */
315 CsrProcess->ServerData[i] = ProcessData;
316
317 /* Move to the next data location */
318 ProcessData = (PVOID)((ULONG_PTR)ProcessData +
319 ServerDll->SizeOfProcessData);
320 }
321 else
322 {
323 /* Nothing for this Process */
324 CsrProcess->ServerData[i] = NULL;
325 }
326 }
327
328 /* Insert the Process */
329 CsrInsertProcess(NULL, CsrProcess);
330
331 /* Activate the Thread */
332 ApiMessage->ReturnValue = NtResumeThread(hThread, NULL);
333
334 /* Release lock and return */
335 CsrReleaseProcessLock();
336 return TRUE;
337 }
338
339 /*++
340 * @name CsrSbForeignSessionComplete
341 *
342 * The CsrSbForeignSessionComplete API is called by the Session Manager
343 * whenever a foreign session is completed (ie: terminated).
344 *
345 * @param ApiMessage
346 * Pointer to the Session Manager API Message.
347 *
348 * @return TRUE in case of success, FALSE otherwise.
349 *
350 * @remarks The CsrSbForeignSessionComplete API is not yet implemented.
351 *
352 *--*/
353 BOOLEAN
354 NTAPI
355 CsrSbForeignSessionComplete(IN PSB_API_MSG ApiMessage)
356 {
357 /* Deprecated/Unimplemented in NT */
358 ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED;
359 return TRUE;
360 }
361
362 /*++
363 * @name CsrSbTerminateSession
364 *
365 * The CsrSbTerminateSession API is called by the Session Manager
366 * whenever a foreign session should be destroyed.
367 *
368 * @param ApiMessage
369 * Pointer to the Session Manager API Message.
370 *
371 * @return TRUE in case of success, FALSE otherwise.
372 *
373 * @remarks The CsrSbTerminateSession API is not yet implemented.
374 *
375 *--*/
376 BOOLEAN
377 NTAPI
378 CsrSbTerminateSession(IN PSB_API_MSG ApiMessage)
379 {
380 ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED;
381 return TRUE;
382 }
383
384 /*++
385 * @name CsrSbCreateProcess
386 *
387 * The CsrSbCreateProcess API is called by the Session Manager
388 * whenever a foreign session is created and a new process should be started.
389 *
390 * @param ApiMessage
391 * Pointer to the Session Manager API Message.
392 *
393 * @return TRUE in case of success, FALSE otherwise.
394 *
395 * @remarks The CsrSbCreateProcess API is not yet implemented.
396 *
397 *--*/
398 BOOLEAN
399 NTAPI
400 CsrSbCreateProcess(IN PSB_API_MSG ApiMessage)
401 {
402 ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED;
403 return TRUE;
404 }
405
406 /*++
407 * @name CsrSbApiHandleConnectionRequest
408 *
409 * The CsrSbApiHandleConnectionRequest routine handles and accepts a new
410 * connection request to the SM API LPC Port.
411 *
412 * @param ApiMessage
413 * Pointer to the incoming CSR API Message which contains the
414 * connection request.
415 *
416 * @return STATUS_SUCCESS in case of success, or status code which caused
417 * the routine to error.
418 *
419 * @remarks None.
420 *
421 *--*/
422 NTSTATUS
423 NTAPI
424 CsrSbApiHandleConnectionRequest(IN PSB_API_MSG Message)
425 {
426 NTSTATUS Status;
427 REMOTE_PORT_VIEW RemotePortView;
428 HANDLE hPort;
429
430 /* Set the Port View Structure Length */
431 RemotePortView.Length = sizeof(REMOTE_PORT_VIEW);
432
433 /* Accept the connection */
434 Status = NtAcceptConnectPort(&hPort,
435 NULL,
436 (PPORT_MESSAGE)Message,
437 TRUE,
438 NULL,
439 &RemotePortView);
440 if (!NT_SUCCESS(Status))
441 {
442 DPRINT1("CSRSS: Sb Accept Connection failed %lx\n", Status);
443 return Status;
444 }
445
446 /* Complete the Connection */
447 Status = NtCompleteConnectPort(hPort);
448 if (!NT_SUCCESS(Status))
449 {
450 DPRINT1("CSRSS: Sb Complete Connection failed %lx\n",Status);
451 }
452
453 /* Return status */
454 return Status;
455 }
456
457 /*++
458 * @name CsrSbApiRequestThread
459 *
460 * The CsrSbApiRequestThread routine handles incoming messages or connection
461 * requests on the SM API LPC Port.
462 *
463 * @param Parameter
464 * System-default user-defined parameter. Unused.
465 *
466 * @return The thread exit code, if the thread is terminated.
467 *
468 * @remarks Before listening on the port, the routine will first attempt
469 * to connect to the user subsystem.
470 *
471 *--*/
472 VOID
473 NTAPI
474 CsrSbApiRequestThread(IN PVOID Parameter)
475 {
476 NTSTATUS Status;
477 SB_API_MSG ReceiveMsg;
478 PSB_API_MSG ReplyMsg = NULL;
479 PVOID PortContext;
480 ULONG MessageType;
481
482 /* Start the loop */
483 while (TRUE)
484 {
485 /* Wait for a message to come in */
486 Status = NtReplyWaitReceivePort(CsrSbApiPort,
487 &PortContext,
488 &ReplyMsg->h,
489 &ReceiveMsg.h);
490
491 /* Check if we didn't get success */
492 if (Status != STATUS_SUCCESS)
493 {
494 /* If we only got a warning, keep going */
495 if (NT_SUCCESS(Status)) continue;
496
497 /* We failed big time, so start out fresh */
498 ReplyMsg = NULL;
499 DPRINT1("CSRSS: ReceivePort failed - Status == %X\n", Status);
500 continue;
501 }
502
503 /* Save the message type */
504 MessageType = ReceiveMsg.h.u2.s2.Type;
505
506 /* Check if this is a connection request */
507 if (MessageType == LPC_CONNECTION_REQUEST)
508 {
509 /* Handle connection request */
510 CsrSbApiHandleConnectionRequest(&ReceiveMsg);
511
512 /* Start over */
513 ReplyMsg = NULL;
514 continue;
515 }
516
517 /* Check if the port died */
518 if (MessageType == LPC_PORT_CLOSED)
519 {
520 /* Close the handle if we have one */
521 if (PortContext) NtClose((HANDLE)PortContext);
522
523 /* Client died, start over */
524 ReplyMsg = NULL;
525 continue;
526 }
527 else if (MessageType == LPC_CLIENT_DIED)
528 {
529 /* Client died, start over */
530 ReplyMsg = NULL;
531 continue;
532 }
533
534 /*
535 * It's an API Message, check if it's within limits. If it's not,
536 * the NT Behaviour is to set this to the Maximum API.
537 */
538 if (ReceiveMsg.ApiNumber > SbpMaxApiNumber)
539 {
540 ReceiveMsg.ApiNumber = SbpMaxApiNumber;
541 DPRINT1("CSRSS: %lx is invalid Sb ApiNumber\n", ReceiveMsg.ApiNumber);
542 }
543
544 /* Reuse the message */
545 ReplyMsg = &ReceiveMsg;
546
547 /* Make sure that the message is supported */
548 if (ReceiveMsg.ApiNumber < SbpMaxApiNumber)
549 {
550 /* Call the API */
551 if (!CsrServerSbApiDispatch[ReceiveMsg.ApiNumber](&ReceiveMsg))
552 {
553 DPRINT1("CSRSS: %s Session Api called and failed\n",
554 CsrServerSbApiName[ReceiveMsg.ApiNumber]);
555
556 /* It failed, so return nothing */
557 ReplyMsg = NULL;
558 }
559 }
560 else
561 {
562 /* We don't support this API Number */
563 ReplyMsg->ReturnValue = STATUS_NOT_IMPLEMENTED;
564 }
565 }
566 }
567
568 /* EOF */