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