35fbb9ceeee050d9fdcde4936afad5d6e7b8e0bc
[reactos.git] / base / system / smss / smloop.c
1 /*
2 * PROJECT: ReactOS Windows-Compatible Session Manager
3 * LICENSE: BSD 2-Clause License
4 * FILE: base/system/smss/smss.c
5 * PURPOSE: Main SMSS Code
6 * PROGRAMMERS: Alex Ionescu
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "smss.h"
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS ********************************************************************/
16
17 typedef struct _SMP_CLIENT_CONTEXT
18 {
19 PVOID Subsystem;
20 HANDLE ProcessHandle;
21 HANDLE PortHandle;
22 ULONG dword10;
23 } SMP_CLIENT_CONTEXT, *PSMP_CLIENT_CONTEXT;
24
25 typedef
26 NTSTATUS
27 (NTAPI *PSM_API_HANDLER)(
28 IN PSM_API_MSG SmApiMsg,
29 IN PSMP_CLIENT_CONTEXT ClientContext,
30 IN HANDLE SmApiPort
31 );
32
33 volatile LONG SmTotalApiThreads;
34 HANDLE SmUniqueProcessId;
35
36 /* API HANDLERS ***************************************************************/
37
38 NTSTATUS
39 NTAPI
40 SmpCreateForeignSession(IN PSM_API_MSG SmApiMsg,
41 IN PSMP_CLIENT_CONTEXT ClientContext,
42 IN HANDLE SmApiPort)
43 {
44 DPRINT1("%s is not yet implemented\n", __FUNCTION__);
45 return STATUS_NOT_IMPLEMENTED;
46 }
47
48 NTSTATUS
49 NTAPI
50 SmpSessionComplete(IN PSM_API_MSG SmApiMsg,
51 IN PSMP_CLIENT_CONTEXT ClientContext,
52 IN HANDLE SmApiPort)
53 {
54 DPRINT1("%s is not yet implemented\n", __FUNCTION__);
55 return STATUS_NOT_IMPLEMENTED;
56 }
57
58 NTSTATUS
59 NTAPI
60 SmpTerminateForeignSession(IN PSM_API_MSG SmApiMsg,
61 IN PSMP_CLIENT_CONTEXT ClientContext,
62 IN HANDLE SmApiPort)
63 {
64 DPRINT1("%s is not yet implemented\n", __FUNCTION__);
65 return STATUS_NOT_IMPLEMENTED;
66 }
67
68 NTSTATUS
69 NTAPI
70 SmpExecPgm(IN PSM_API_MSG SmApiMsg,
71 IN PSMP_CLIENT_CONTEXT ClientContext,
72 IN HANDLE SmApiPort)
73 {
74 HANDLE ProcessHandle;
75 NTSTATUS Status;
76 PSM_EXEC_PGM_MSG SmExecPgm;
77 RTL_USER_PROCESS_INFORMATION ProcessInformation;
78 OBJECT_ATTRIBUTES ObjectAttributes;
79
80 /* Open the client process */
81 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
82 Status = NtOpenProcess(&ProcessHandle,
83 PROCESS_DUP_HANDLE,
84 &ObjectAttributes,
85 &SmApiMsg->h.ClientId);
86 if (!NT_SUCCESS(Status))
87 {
88 /* Fail */
89 DPRINT1("SmExecPgm: NtOpenProcess Failed %lx\n", Status);
90 return Status;
91 }
92
93 /* Copy the process information out of the message */
94 SmExecPgm = &SmApiMsg->u.ExecPgm;
95 ProcessInformation = SmExecPgm->ProcessInformation;
96
97 /* Duplicate the process handle */
98 Status = NtDuplicateObject(ProcessHandle,
99 SmExecPgm->ProcessInformation.ProcessHandle,
100 NtCurrentProcess(),
101 &ProcessInformation.ProcessHandle,
102 PROCESS_ALL_ACCESS,
103 0,
104 0);
105 if (!NT_SUCCESS(Status))
106 {
107 /* Close the handle and fail */
108 NtClose(ProcessHandle);
109 DPRINT1("SmExecPgm: NtDuplicateObject (Process) Failed %lx\n", Status);
110 return Status;
111 }
112
113 /* Duplicate the thread handle */
114 Status = NtDuplicateObject(ProcessHandle,
115 SmExecPgm->ProcessInformation.ThreadHandle,
116 NtCurrentProcess(),
117 &ProcessInformation.ThreadHandle,
118 THREAD_ALL_ACCESS,
119 0,
120 0);
121 if (!NT_SUCCESS(Status))
122 {
123 /* Close both handles and fail */
124 NtClose(ProcessInformation.ProcessHandle);
125 NtClose(ProcessHandle);
126 DPRINT1("SmExecPgm: NtDuplicateObject (Thread) Failed %lx\n", Status);
127 return Status;
128 }
129
130 /* Close the process handle and call the internal client API */
131 NtClose(ProcessHandle);
132 return SmpSbCreateSession(NULL,
133 NULL,
134 &ProcessInformation,
135 0,
136 SmExecPgm->DebugFlag ? &SmApiMsg->h.ClientId : NULL);
137 }
138
139 NTSTATUS
140 NTAPI
141 SmpLoadDeferedSubsystem(IN PSM_API_MSG SmApiMsg,
142 IN PSMP_CLIENT_CONTEXT ClientContext,
143 IN HANDLE SmApiPort)
144 {
145 DPRINT1("%s is not yet implemented\n", __FUNCTION__);
146 return STATUS_NOT_IMPLEMENTED;
147 }
148
149 NTSTATUS
150 NTAPI
151 SmpStartCsr(IN PSM_API_MSG SmApiMsg,
152 IN PSMP_CLIENT_CONTEXT ClientContext,
153 IN HANDLE SmApiPort)
154 {
155 DPRINT1("%s is not yet implemented\n", __FUNCTION__);
156 return STATUS_NOT_IMPLEMENTED;
157 }
158
159 NTSTATUS
160 NTAPI
161 SmpStopCsr(IN PSM_API_MSG SmApiMsg,
162 IN PSMP_CLIENT_CONTEXT ClientContext,
163 IN HANDLE SmApiPort)
164 {
165 DPRINT1("%s is not yet implemented\n", __FUNCTION__);
166 return STATUS_NOT_IMPLEMENTED;
167 }
168
169 PSM_API_HANDLER SmpApiDispatch[SmMaxApiNumber] =
170 {
171 SmpCreateForeignSession,
172 SmpSessionComplete,
173 SmpTerminateForeignSession,
174 SmpExecPgm,
175 SmpLoadDeferedSubsystem,
176 SmpStartCsr,
177 SmpStopCsr
178 };
179
180 /* FUNCTIONS ******************************************************************/
181
182 NTSTATUS
183 NTAPI
184 SmpHandleConnectionRequest(IN HANDLE SmApiPort,
185 IN PSB_API_MSG SbApiMsg)
186 {
187 BOOLEAN Accept = TRUE;
188 HANDLE PortHandle, ProcessHandle;
189 ULONG SessionId;
190 UNICODE_STRING SubsystemPort;
191 SMP_CLIENT_CONTEXT *ClientContext;
192 NTSTATUS Status;
193 OBJECT_ATTRIBUTES ObjectAttributes;
194 REMOTE_PORT_VIEW PortView;
195 SECURITY_QUALITY_OF_SERVICE SecurityQos;
196 PSMP_SUBSYSTEM CidSubsystem, TypeSubsystem;
197
198 /* Initialize QoS data */
199 SecurityQos.ImpersonationLevel = SecurityIdentification;
200 SecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
201 SecurityQos.EffectiveOnly = TRUE;
202
203 /* Check if this is SM connecting to itself */
204 if (SbApiMsg->h.ClientId.UniqueProcess == SmUniqueProcessId)
205 {
206 /* No need to get any handle -- assume session 0 */
207 ProcessHandle = NULL;
208 SessionId = 0;
209 }
210 else
211 {
212 /* Reference the foreign process */
213 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
214 Status = NtOpenProcess(&ProcessHandle,
215 PROCESS_QUERY_INFORMATION,
216 &ObjectAttributes,
217 &SbApiMsg->h.ClientId);
218 if (!NT_SUCCESS(Status)) Accept = FALSE;
219
220 /* Get its session ID */
221 SmpGetProcessMuSessionId(ProcessHandle, &SessionId);
222 }
223
224 /* See if we already know about the caller's subystem */
225 CidSubsystem = SmpLocateKnownSubSysByCid(&SbApiMsg->h.ClientId);
226 if ((CidSubsystem) && (Accept))
227 {
228 /* Check if we already have a subsystem for this kind of image */
229 TypeSubsystem = SmpLocateKnownSubSysByType(SessionId,
230 SbApiMsg->ConnectionInfo.SubsystemType);
231 if (TypeSubsystem == CidSubsystem)
232 {
233 /* Someone is trying to take control of an existing subsystem, fail */
234 Accept = FALSE;
235 DPRINT1("SMSS: Connection from SubSystem rejected\n");
236 DPRINT1("SMSS: Image type already being served\n");
237 }
238 else
239 {
240 /* Set this image type as the type for this subsystem */
241 CidSubsystem->ImageType = SbApiMsg->ConnectionInfo.SubsystemType;
242 }
243
244 /* Drop the reference we had acquired */
245 if (TypeSubsystem) SmpDereferenceSubsystem(TypeSubsystem);
246 }
247
248 /* Check if we'll be accepting the connection */
249 if (Accept)
250 {
251 /* We will, so create a client context for it */
252 ClientContext = RtlAllocateHeap(SmpHeap, 0, sizeof(SMP_CLIENT_CONTEXT));
253 if (ClientContext)
254 {
255 ClientContext->ProcessHandle = ProcessHandle;
256 ClientContext->Subsystem = CidSubsystem;
257 ClientContext->dword10 = 0;
258 ClientContext->PortHandle = NULL;
259 }
260 else
261 {
262 /* Failed to allocate a client context, so reject the connection */
263 DPRINT1("Rejecting connectiond due to lack of memory\n");
264 Accept = FALSE;
265 }
266 }
267 else
268 {
269 /* Use a bogus context since we're going to reject the message */
270 ClientContext = (PSMP_CLIENT_CONTEXT)SbApiMsg;
271 }
272
273 /* Now send the actual accept reply (which could be a rejection) */
274 PortView.Length = sizeof(PortView);
275 Status = NtAcceptConnectPort(&PortHandle,
276 ClientContext,
277 &SbApiMsg->h,
278 Accept,
279 NULL,
280 &PortView);
281 if (!(Accept) || !(NT_SUCCESS(Status)))
282 {
283 /* Close the process handle, reference the subsystem, and exit */
284 DPRINT1("Accept failed or rejected: %lx\n", Status);
285 if (ClientContext != (PVOID)SbApiMsg) RtlFreeHeap(SmpHeap, 0, ClientContext);
286 if (ProcessHandle) NtClose(ProcessHandle);
287 if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem);
288 return Status;
289 }
290
291 /* Save the port handle now that we've accepted it */
292 if (ClientContext) ClientContext->PortHandle = PortHandle;
293 if (CidSubsystem) CidSubsystem->PortHandle = PortHandle;
294
295 /* Complete the port connection */
296 Status = NtCompleteConnectPort(PortHandle);
297 if ((NT_SUCCESS(Status)) && (CidSubsystem))
298 {
299 /* This was an actual subsystem, so connect back to it */
300 SbApiMsg->ConnectionInfo.SbApiPortName[119] = UNICODE_NULL;
301 RtlCreateUnicodeString(&SubsystemPort,
302 SbApiMsg->ConnectionInfo.SbApiPortName);
303 Status = NtConnectPort(&CidSubsystem->SbApiPort,
304 &SubsystemPort,
305 &SecurityQos,
306 NULL,
307 NULL,
308 NULL,
309 NULL,
310 NULL);
311 if (!NT_SUCCESS(Status))
312 {
313 DPRINT1("SMSS: Connect back to Sb %wZ failed %lx\n", &SubsystemPort, Status);
314 }
315 RtlFreeUnicodeString(&SubsystemPort);
316
317 /* Now that we're connected, signal the event handle */
318 NtSetEvent(CidSubsystem->Event, NULL);
319 }
320 else if (CidSubsystem)
321 {
322 /* We failed to complete the connection, so clear the port handle */
323 DPRINT1("Completing the connection failed: %lx\n", Status);
324 CidSubsystem->PortHandle = NULL;
325 }
326
327 /* Dereference the subsystem and return the result */
328 if (CidSubsystem) SmpDereferenceSubsystem(CidSubsystem);
329 return Status;
330 }
331
332 ULONG
333 NTAPI
334 SmpApiLoop(IN PVOID Parameter)
335 {
336 HANDLE SmApiPort = (HANDLE)Parameter;
337 NTSTATUS Status;
338 PSMP_CLIENT_CONTEXT ClientContext;
339 PSM_API_MSG ReplyMsg = NULL;
340 SM_API_MSG RequestMsg;
341 PROCESS_BASIC_INFORMATION ProcessInformation;
342 LARGE_INTEGER Timeout;
343
344 /* Increase the number of API threads for throttling code for later */
345 _InterlockedExchangeAdd(&SmTotalApiThreads, 1);
346
347 /* Mark us critical */
348 RtlSetThreadIsCritical(TRUE, NULL, TRUE);
349
350 /* Set the PID of the SM process itself for later checking */
351 NtQueryInformationProcess(NtCurrentProcess(),
352 ProcessBasicInformation,
353 &ProcessInformation,
354 sizeof(ProcessInformation),
355 NULL);
356 SmUniqueProcessId = (HANDLE)ProcessInformation.UniqueProcessId;
357
358 /* Now process incoming messages */
359 while (TRUE)
360 {
361 /* Begin waiting on a request */
362 Status = NtReplyWaitReceivePort(SmApiPort,
363 (PVOID*)&ClientContext,
364 &ReplyMsg->h,
365 &RequestMsg.h);
366 if (Status == STATUS_NO_MEMORY)
367 {
368 /* Ran out of memory, so do a little timeout and try again */
369 if (ReplyMsg) DPRINT1("SMSS: Failed to reply to calling thread, retrying.\n");
370 Timeout.QuadPart = -50000000;
371 NtDelayExecution(FALSE, &Timeout);
372 continue;
373 }
374
375 /* Check what kind of request we received */
376 switch (RequestMsg.h.u2.s2.Type)
377 {
378 /* A new connection */
379 case LPC_CONNECTION_REQUEST:
380 /* Create the right structures for it */
381 SmpHandleConnectionRequest(SmApiPort, (PSB_API_MSG)&RequestMsg);
382 ReplyMsg = NULL;
383 break;
384
385 /* A closed connection */
386 case LPC_PORT_CLOSED:
387 /* Destroy any state we had for this client */
388 DPRINT1("Port closed\n");
389 //if (ClientContext) SmpPushDeferredClientContext(ClientContext);
390 ReplyMsg = NULL;
391 break;
392
393 /* An actual API message */
394 default:
395 if (!ClientContext)
396 {
397 ReplyMsg = NULL;
398 break;
399 }
400
401 RequestMsg.ReturnValue = STATUS_PENDING;
402
403 /* Check if the API is valid */
404 if (RequestMsg.ApiNumber >= SmMaxApiNumber)
405 {
406 /* It isn't, fail */
407 DPRINT1("Invalid API: %lx\n", RequestMsg.ApiNumber);
408 Status = STATUS_NOT_IMPLEMENTED;
409 }
410 else if ((RequestMsg.ApiNumber <= SmTerminateForeignSessionApi) &&
411 !(ClientContext->Subsystem))
412 {
413 /* It's valid, but doesn't have a subsystem with it */
414 DPRINT1("Invalid session API\n");
415 Status = STATUS_INVALID_PARAMETER;
416 }
417 else
418 {
419 /* It's totally okay, so call the dispatcher for it */
420 Status = SmpApiDispatch[RequestMsg.ApiNumber](&RequestMsg,
421 ClientContext,
422 SmApiPort);
423 }
424
425 /* Write the result valud and return the message back */
426 RequestMsg.ReturnValue = Status;
427 ReplyMsg = &RequestMsg;
428 break;
429 }
430 }
431 return STATUS_SUCCESS;
432 }