28bd66f22dc8dcc0632d54e04ccdeb43ca9f51c3
[reactos.git] / base / system / smss / smss.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 #include <pseh/pseh2.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /* GLOBALS ********************************************************************/
19
20 UNICODE_STRING SmpSystemRoot;
21 ULONG AttachedSessionId = -1;
22 BOOLEAN SmpDebug, SmpEnableDots;
23 HANDLE SmApiPort;
24 HANDLE SmpInitialCommandProcessId;
25
26 /* FUNCTIONS ******************************************************************/
27
28 /* GCC's incompetence strikes again */
29 VOID
30 sprintf_nt(IN PCHAR Buffer,
31 IN PCHAR Format,
32 IN ...)
33 {
34 va_list ap;
35 va_start(ap, Format);
36 sprintf(Buffer, Format, ap);
37 va_end(ap);
38 }
39
40 NTSTATUS
41 NTAPI
42 SmpExecuteImage(IN PUNICODE_STRING FileName,
43 IN PUNICODE_STRING Directory,
44 IN PUNICODE_STRING CommandLine,
45 IN ULONG MuSessionId,
46 IN ULONG Flags,
47 IN PRTL_USER_PROCESS_INFORMATION ProcessInformation)
48 {
49 PRTL_USER_PROCESS_INFORMATION ProcessInfo;
50 NTSTATUS Status;
51 RTL_USER_PROCESS_INFORMATION LocalProcessInfo;
52 PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
53
54 /* Use the input process information if we have it, otherwise use local */
55 ProcessInfo = ProcessInformation;
56 if (!ProcessInfo) ProcessInfo = &LocalProcessInfo;
57
58 /* Create parameters for the target process */
59 Status = RtlCreateProcessParameters(&ProcessParameters,
60 FileName,
61 SmpDefaultLibPath.Length ?
62 &SmpDefaultLibPath : NULL,
63 Directory,
64 CommandLine,
65 SmpDefaultEnvironment,
66 NULL,
67 NULL,
68 NULL,
69 0);
70 if (!NT_SUCCESS(Status))
71 {
72 /* This is a pretty bad failure. ASSERT on checked builds and exit */
73 ASSERTMSG("RtlCreateProcessParameters", NT_SUCCESS(Status));
74 DPRINT1("SMSS: RtlCreateProcessParameters failed for %wZ - Status == %lx\n",
75 FileName, Status);
76 return Status;
77 }
78
79 /* Set the size field as required */
80 ProcessInfo->Size = sizeof(RTL_USER_PROCESS_INFORMATION);
81
82 /* Check if the debug flag was requested */
83 if (Flags & SMP_DEBUG_FLAG)
84 {
85 /* Write it in the process parameters */
86 ProcessParameters->DebugFlags = 1;
87 }
88 else
89 {
90 /* Otherwise inherit the flag that was passed to SMSS itself */
91 ProcessParameters->DebugFlags = SmpDebug;
92 }
93
94 /* Subsystems get the first 1MB of memory reserved for DOS/IVT purposes */
95 if (Flags & SMP_SUBSYSTEM_FLAG)
96 {
97 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB;
98 }
99
100 /* And always force NX for anything that SMSS launches */
101 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_NX;
102
103 /* Now create the process */
104 Status = RtlCreateUserProcess(FileName,
105 OBJ_CASE_INSENSITIVE,
106 ProcessParameters,
107 NULL,
108 NULL,
109 NULL,
110 FALSE,
111 NULL,
112 NULL,
113 ProcessInfo);
114 RtlDestroyProcessParameters(ProcessParameters);
115 if (!NT_SUCCESS(Status))
116 {
117 /* If we couldn't create it, fail back to the caller */
118 DPRINT1("SMSS: Failed load of %wZ - Status == %lx\n",
119 FileName, Status);
120 return Status;
121 }
122
123 /* Associate a session with this process */
124 Status = SmpSetProcessMuSessionId(ProcessInfo->ProcessHandle, MuSessionId);
125
126 /* If the application is deferred (suspended), there's nothing to do */
127 if (Flags & SMP_DEFERRED_FLAG) return Status;
128
129 /* Otherwise, get ready to start it, but make sure it's a native app */
130 if (ProcessInfo->ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_NATIVE)
131 {
132 /* Resume it */
133 NtResumeThread(ProcessInfo->ThreadHandle, NULL);
134 if (!(Flags & SMP_ASYNC_FLAG))
135 {
136 /* Block on it unless Async was requested */
137 NtWaitForSingleObject(ProcessInfo->ThreadHandle, FALSE, NULL);
138 }
139
140 /* It's up and running now, close our handles */
141 NtClose(ProcessInfo->ThreadHandle);
142 NtClose(ProcessInfo->ProcessHandle);
143 }
144 else
145 {
146 /* This image is invalid, so kill it, close our handles, and fail */
147 Status = STATUS_INVALID_IMAGE_FORMAT;
148 NtTerminateProcess(ProcessInfo->ProcessHandle, Status);
149 NtWaitForSingleObject(ProcessInfo->ThreadHandle, 0, 0);
150 NtClose(ProcessInfo->ThreadHandle);
151 NtClose(ProcessInfo->ProcessHandle);
152 DPRINT1("SMSS: Not an NT image - %wZ\n", FileName);
153 }
154
155 /* Return the outcome of the process create */
156 return Status;
157 }
158
159 NTSTATUS
160 NTAPI
161 SmpInvokeAutoChk(IN PUNICODE_STRING FileName,
162 IN PUNICODE_STRING Directory,
163 IN PUNICODE_STRING Arguments,
164 IN ULONG Flags)
165 {
166 ANSI_STRING MessageString;
167 CHAR MessageBuffer[256];
168 UNICODE_STRING Destination;
169 WCHAR Buffer[1024];
170 BOOLEAN BootState, BootOkay, ShutdownOkay;
171
172 /* Check if autochk should show dots (if the user booted with /SOS) */
173 if (SmpQueryRegistrySosOption()) SmpEnableDots = FALSE;
174
175 /* Make sure autochk was actually found */
176 if (Flags & SMP_INVALID_PATH)
177 {
178 /* It wasn't, so create an error message to print on the screen */
179 sprintf_nt(MessageBuffer,
180 "%wZ program not found - skipping AUTOCHECK\r\n",
181 FileName);
182 RtlInitAnsiString(&MessageString, MessageBuffer);
183 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Destination,
184 &MessageString,
185 TRUE)))
186 {
187 /* And show it */
188 NtDisplayString(&Destination);
189 RtlFreeUnicodeString(&Destination);
190 }
191 }
192 else
193 {
194 /* Autochk is there, so record the BSD state */
195 BootState = SmpSaveAndClearBootStatusData(&BootOkay, &ShutdownOkay);
196
197 /* Build the path to autochk and place its arguments */
198 RtlInitEmptyUnicodeString(&Destination, Buffer, sizeof(Buffer));
199 RtlAppendUnicodeStringToString(&Destination, FileName);
200 RtlAppendUnicodeToString(&Destination, L" ");
201 RtlAppendUnicodeStringToString(&Destination, Arguments);
202
203 /* Execute it */
204 SmpExecuteImage(FileName,
205 Directory,
206 &Destination,
207 0,
208 Flags & ~SMP_AUTOCHK_FLAG,
209 NULL);
210
211 /* Restore the BSD state */
212 if (BootState) SmpRestoreBootStatusData(BootOkay, ShutdownOkay);
213 }
214
215 /* We're all done! */
216 return STATUS_SUCCESS;
217 }
218
219 NTSTATUS
220 NTAPI
221 SmpExecuteCommand(IN PUNICODE_STRING CommandLine,
222 IN ULONG MuSessionId,
223 OUT PHANDLE ProcessId,
224 IN ULONG Flags)
225 {
226 NTSTATUS Status;
227 UNICODE_STRING Arguments, Directory, FileName;
228
229 /* There's no longer a debugging subsystem */
230 if (Flags & SMP_DEBUG_FLAG) return STATUS_SUCCESS;
231
232 /* Parse the command line to see what execution flags are requested */
233 Status = SmpParseCommandLine(CommandLine,
234 &Flags,
235 &FileName,
236 &Directory,
237 &Arguments);
238 if (!NT_SUCCESS(Status))
239 {
240 /* Fail if we couldn't do that */
241 DPRINT1("SMSS: SmpParseCommandLine( %wZ ) failed - Status == %lx\n",
242 CommandLine, Status);
243 return Status;
244 }
245
246 /* Check if autochk is requested */
247 if (Flags & SMP_AUTOCHK_FLAG)
248 {
249 /* Run it */
250 Status = SmpInvokeAutoChk(&FileName, &Directory, &Arguments, Flags);
251 }
252 else if (Flags & SMP_SUBSYSTEM_FLAG)
253 {
254 Status = SmpLoadSubSystem(&FileName,
255 &Directory,
256 CommandLine,
257 MuSessionId,
258 ProcessId,
259 Flags);
260 }
261 else if (Flags & SMP_INVALID_PATH)
262 {
263 /* An invalid image was specified, fail */
264 DPRINT1("SMSS: Image file (%wZ) not found\n", &FileName);
265 Status = STATUS_OBJECT_NAME_NOT_FOUND;
266 }
267 else
268 {
269 /* An actual image name was present -- execute it */
270 Status = SmpExecuteImage(&FileName,
271 &Directory,
272 CommandLine,
273 MuSessionId,
274 Flags,
275 NULL);
276 }
277
278 /* Free all the token parameters */
279 if (FileName.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, FileName.Buffer);
280 if (Directory.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Directory.Buffer);
281 if (Arguments.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Arguments.Buffer);
282
283 /* Return to the caller */
284 if (!NT_SUCCESS(Status))
285 {
286 DPRINT1("SMSS: Command '%wZ' failed - Status == %x\n",
287 CommandLine, Status);
288 }
289 return Status;
290 }
291
292 NTSTATUS
293 NTAPI
294 SmpExecuteInitialCommand(IN ULONG MuSessionId,
295 IN PUNICODE_STRING InitialCommand,
296 IN HANDLE InitialCommandProcess,
297 OUT PHANDLE ReturnPid)
298 {
299 NTSTATUS Status;
300 RTL_USER_PROCESS_INFORMATION ProcessInfo;
301 UNICODE_STRING Arguments, ImageFileDirectory, ImageFileName;
302 ULONG Flags = 0;
303
304 /* Check if we haven't yet connected to ourselves */
305 if (!SmApiPort)
306 {
307 /* Connect to ourselves, as a client */
308 Status = SmConnectToSm(0, 0, 0, &SmApiPort);
309 if (!NT_SUCCESS(Status))
310 {
311 DPRINT1("SMSS: Unable to connect to SM - Status == %lx\n", Status);
312 return Status;
313 }
314 }
315
316 /* Parse the initial command line */
317 Status = SmpParseCommandLine(InitialCommand,
318 &Flags,
319 &ImageFileName,
320 &ImageFileDirectory,
321 &Arguments);
322 if (Flags & SMP_INVALID_PATH)
323 {
324 /* Fail if it doesn't exist */
325 DPRINT1("SMSS: Initial command image (%wZ) not found\n", &ImageFileName);
326 if (ImageFileName.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, ImageFileName.Buffer);
327 return STATUS_OBJECT_NAME_NOT_FOUND;
328 }
329
330 /* And fail if any other reason is also true */
331 if (!NT_SUCCESS(Status))
332 {
333 DPRINT1("SMSS: SmpParseCommandLine( %wZ ) failed - Status == %lx\n",
334 InitialCommand, Status);
335 return Status;
336 }
337
338 /* Execute the initial command -- but defer its full execution */
339 Status = SmpExecuteImage(&ImageFileName,
340 &ImageFileDirectory,
341 InitialCommand,
342 MuSessionId,
343 SMP_DEFERRED_FLAG,
344 &ProcessInfo);
345
346 /* Free any buffers we had lying around */
347 if (ImageFileName.Buffer)
348 {
349 RtlFreeHeap(RtlGetProcessHeap(), 0, ImageFileName.Buffer);
350 }
351 if (ImageFileDirectory.Buffer)
352 {
353 RtlFreeHeap(RtlGetProcessHeap(), 0, ImageFileDirectory.Buffer);
354 }
355 if (Arguments.Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Arguments.Buffer);
356
357 /* Bail out if we couldn't execute the initial command */
358 if (!NT_SUCCESS(Status)) return Status;
359
360 /* Now duplicate the handle to this process */
361 Status = NtDuplicateObject(NtCurrentProcess(),
362 ProcessInfo.ProcessHandle,
363 NtCurrentProcess(),
364 InitialCommandProcess,
365 PROCESS_ALL_ACCESS,
366 0,
367 0);
368 if (!NT_SUCCESS(Status))
369 {
370 /* Kill it utterly if duplication failed */
371 DPRINT1("SMSS: DupObject Failed. Status == %lx\n", Status);
372 NtTerminateProcess(ProcessInfo.ProcessHandle, Status);
373 NtResumeThread(ProcessInfo.ThreadHandle, NULL);
374 NtClose(ProcessInfo.ThreadHandle);
375 NtClose(ProcessInfo.ProcessHandle);
376 return Status;
377 }
378
379 /* Return PID to the caller, and set this as the initial command PID */
380 if (ReturnPid) *ReturnPid = ProcessInfo.ClientId.UniqueProcess;
381 if (!MuSessionId) SmpInitialCommandProcessId = ProcessInfo.ClientId.UniqueProcess;
382
383 /* Now call our server execution function to wrap up its initialization */
384 Status = SmExecPgm(SmApiPort, &ProcessInfo, FALSE);
385 if (!NT_SUCCESS(Status)) DPRINT1("SMSS: SmExecPgm Failed. Status == %lx\n", Status);
386 return Status;
387 }
388
389 NTSTATUS
390 NTAPI
391 SmpTerminate(IN PULONG_PTR Parameters,
392 IN ULONG ParameterMask,
393 IN ULONG ParameterCount)
394 {
395 NTSTATUS Status;
396 BOOLEAN Old;
397 ULONG Response;
398
399 /* Give the shutdown privilege to the thread */
400 if (RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, TRUE, &Old) ==
401 STATUS_NO_TOKEN)
402 {
403 /* Thread doesn't have a token, give it to the entire process */
404 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &Old);
405 }
406
407 /* Take down the process/machine with a hard error */
408 Status = NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED,
409 ParameterCount,
410 ParameterMask,
411 Parameters,
412 OptionShutdownSystem,
413 &Response);
414
415 /* Terminate the process if the hard error didn't already */
416 return NtTerminateProcess(NtCurrentProcess(), Status);
417 }
418
419 LONG
420 SmpUnhandledExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo)
421 {
422 ULONG_PTR Parameters[4];
423 UNICODE_STRING DestinationString;
424
425 /* Print and breakpoint into the debugger */
426 DbgPrint("SMSS: Unhandled exception - Status == %x IP == %p\n",
427 ExceptionInfo->ExceptionRecord->ExceptionCode,
428 ExceptionInfo->ExceptionRecord->ExceptionAddress);
429 DbgPrint(" Memory Address: %x Read/Write: %x\n",
430 ExceptionInfo->ExceptionRecord->ExceptionInformation[0],
431 ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
432 DbgBreakPoint();
433
434 /* Build the hard error and terminate */
435 RtlInitUnicodeString(&DestinationString, L"Unhandled Exception in Session Manager");
436 Parameters[0] = (ULONG_PTR)&DestinationString;
437 Parameters[1] = ExceptionInfo->ExceptionRecord->ExceptionCode;
438 Parameters[2] = (ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress;
439 Parameters[3] = (ULONG_PTR)ExceptionInfo->ContextRecord;
440 SmpTerminate(Parameters, 1, RTL_NUMBER_OF(Parameters));
441
442 /* We should never get here */
443 ASSERT(FALSE);
444 return EXCEPTION_EXECUTE_HANDLER;
445 }
446
447 NTSTATUS
448 __cdecl
449 _main(IN INT argc,
450 IN PCHAR argv[],
451 IN PCHAR envp[],
452 IN ULONG DebugFlag)
453 {
454 NTSTATUS Status;
455 KPRIORITY SetBasePriority;
456 ULONG_PTR Parameters[4];
457 HANDLE Handles[2];
458 PVOID State;
459 ULONG Flags;
460 PROCESS_BASIC_INFORMATION ProcessInfo;
461 UNICODE_STRING DbgString, InitialCommand;
462
463 /* Make us critical */
464 RtlSetProcessIsCritical(TRUE, NULL, FALSE);
465 RtlSetThreadIsCritical(TRUE, NULL, FALSE);
466
467 /* Raise our priority */
468 SetBasePriority = 11;
469 Status = NtSetInformationProcess(NtCurrentProcess(),
470 ProcessBasePriority,
471 (PVOID)&SetBasePriority,
472 sizeof(SetBasePriority));
473 ASSERT(NT_SUCCESS(Status));
474
475 /* Save the debug flag if it was passed */
476 if (DebugFlag) SmpDebug = DebugFlag != 0;
477
478 /* Build the hard error parameters */
479 Parameters[0] = (ULONG_PTR)&DbgString;
480 Parameters[1] = Parameters[2] = Parameters[3] = 0;
481
482 /* Enter SEH so we can terminate correctly if anything goes wrong */
483 _SEH2_TRY
484 {
485 /* Initialize SMSS */
486 Status = SmpInit(&InitialCommand, Handles);
487 if (!NT_SUCCESS(Status))
488 {
489 DPRINT1("SMSS: SmpInit return failure - Status == %x\n", Status);
490 RtlInitUnicodeString(&DbgString, L"Session Manager Initialization");
491 Parameters[1] = Status;
492 _SEH2_LEAVE;
493 }
494
495 /* Get the global flags */
496 Status = NtQuerySystemInformation(SystemFlagsInformation,
497 &Flags,
498 sizeof(Flags),
499 NULL);
500 ASSERT(NT_SUCCESS(Status));
501
502 /* Before executing the initial command check if the debug flag is on */
503 if (Flags & (FLG_DEBUG_INITIAL_COMMAND | FLG_DEBUG_INITIAL_COMMAND_EX))
504 {
505 /* SMSS should launch ntsd with a few parameters at this point */
506 DPRINT1("Global Flags Set to SMSS Debugging: Not yet supported\n");
507 }
508
509 /* Execute the initial command (Winlogon.exe) */
510 Status = SmpExecuteInitialCommand(0, &InitialCommand, &Handles[1], NULL);
511 if (!NT_SUCCESS(Status))
512 {
513 /* Fail and raise a hard error */
514 DPRINT1("SMSS: Execute Initial Command failed\n");
515 RtlInitUnicodeString(&DbgString,
516 L"Session Manager ExecuteInitialCommand");
517 Parameters[1] = Status;
518 _SEH2_LEAVE;
519 }
520
521 /* Check if we're already attached to a session */
522 Status = SmpAcquirePrivilege(SE_LOAD_DRIVER_PRIVILEGE, &State);
523 if (AttachedSessionId != -1)
524 {
525 /* Detach from it, we should be in no session right now */
526 Status = NtSetSystemInformation(SystemSessionDetach,
527 &AttachedSessionId,
528 sizeof(AttachedSessionId));
529 ASSERT(NT_SUCCESS(Status));
530 AttachedSessionId = -1;
531 }
532 SmpReleasePrivilege(State);
533
534 /* Wait on either CSRSS or Winlogon to die */
535 Status = NtWaitForMultipleObjects(RTL_NUMBER_OF(Handles),
536 Handles,
537 WaitAny,
538 FALSE,
539 NULL);
540 if (Status == STATUS_WAIT_0)
541 {
542 /* CSRSS is dead, get exit code and prepare for the hard error */
543 RtlInitUnicodeString(&DbgString, L"Windows SubSystem");
544 Status = NtQueryInformationProcess(Handles[0],
545 ProcessBasicInformation,
546 &ProcessInfo,
547 sizeof(ProcessInfo),
548 NULL);
549 DPRINT1("SMSS: Windows subsystem terminated when it wasn't supposed to.\n");
550 }
551 else
552 {
553 /* The initial command is dead or we have another failure */
554 RtlInitUnicodeString(&DbgString, L"Windows Logon Process");
555 if (Status == STATUS_WAIT_1)
556 {
557 /* Winlogon.exe got terminated, get its exit code */
558 Status = NtQueryInformationProcess(Handles[1],
559 ProcessBasicInformation,
560 &ProcessInfo,
561 sizeof(ProcessInfo),
562 NULL);
563 }
564 else
565 {
566 /* Something else satisfied our wait, so set the wait status */
567 ProcessInfo.ExitStatus = Status;
568 Status = STATUS_SUCCESS;
569 }
570 DPRINT1("SMSS: Initial command '%wZ' terminated when it wasn't supposed to.\n",
571 &InitialCommand);
572 }
573
574 /* Check if NtQueryInformationProcess was successful */
575 if (NT_SUCCESS(Status))
576 {
577 /* Then we must have a valid exit status in the structure, use it */
578 Parameters[1] = ProcessInfo.ExitStatus;
579 }
580 else
581 {
582 /* We really don't know what happened, so set a generic error */
583 Parameters[1] = STATUS_UNSUCCESSFUL;
584 }
585 }
586 _SEH2_EXCEPT(SmpUnhandledExceptionFilter(_SEH2_GetExceptionInformation()))
587 {
588 /* The filter should never return here */
589 ASSERT(FALSE);
590 }
591 _SEH2_END;
592
593 /* Something in the init loop failed, terminate SMSS */
594 return SmpTerminate(Parameters, 1, RTL_NUMBER_OF(Parameters));
595 }
596
597 /* EOF */