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