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