Sync with trunk r63647.
[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 hould never get here */
443 ASSERT(FALSE);
444 return EXCEPTION_EXECUTE_HANDLER;
445 }
446
447 NTSTATUS
448 _main(IN INT argc,
449 IN PCHAR argv[],
450 IN PCHAR envp[],
451 IN ULONG DebugFlag)
452 {
453 NTSTATUS Status;
454 KPRIORITY SetBasePriority;
455 ULONG_PTR Parameters[4];
456 HANDLE Handles[2];
457 PVOID State;
458 ULONG Flags;
459 PROCESS_BASIC_INFORMATION ProcessInfo;
460 UNICODE_STRING DbgString, InitialCommand;
461
462 /* Make us critical */
463 RtlSetProcessIsCritical(TRUE, NULL, FALSE);
464 RtlSetThreadIsCritical(TRUE, NULL, FALSE);
465
466 /* Raise our priority */
467 SetBasePriority = 11;
468 Status = NtSetInformationProcess(NtCurrentProcess(),
469 ProcessBasePriority,
470 (PVOID)&SetBasePriority,
471 sizeof(SetBasePriority));
472 ASSERT(NT_SUCCESS(Status));
473
474 /* Save the debug flag if it was passed */
475 if (DebugFlag) SmpDebug = DebugFlag != 0;
476
477 /* Build the hard error parameters */
478 Parameters[0] = (ULONG_PTR)&DbgString;
479 Parameters[1] = Parameters[2] = Parameters[3] = 0;
480
481 /* Enter SEH so we can terminate correctly if anything goes wrong */
482 _SEH2_TRY
483 {
484 /* Initialize SMSS */
485 Status = SmpInit(&InitialCommand, Handles);
486 if (!NT_SUCCESS(Status))
487 {
488 DPRINT1("SMSS: SmpInit return failure - Status == %x\n", Status);
489 RtlInitUnicodeString(&DbgString, L"Session Manager Initialization");
490 Parameters[1] = Status;
491 _SEH2_LEAVE;
492 }
493
494 /* Get the global flags */
495 Status = NtQuerySystemInformation(SystemFlagsInformation,
496 &Flags,
497 sizeof(Flags),
498 NULL);
499 ASSERT(NT_SUCCESS(Status));
500
501 /* Before executing the initial command check if the debug flag is on */
502 if (Flags & (FLG_DEBUG_INITIAL_COMMAND | FLG_DEBUG_INITIAL_COMMAND_EX))
503 {
504 /* SMSS should launch ntsd with a few parameters at this point */
505 DPRINT1("Global Flags Set to SMSS Debugging: Not yet supported\n");
506 }
507
508 /* Execute the initial command (Winlogon.exe) */
509 Status = SmpExecuteInitialCommand(0, &InitialCommand, &Handles[1], NULL);
510 if (!NT_SUCCESS(Status))
511 {
512 /* Fail and raise a hard error */
513 DPRINT1("SMSS: Execute Initial Command failed\n");
514 RtlInitUnicodeString(&DbgString,
515 L"Session Manager ExecuteInitialCommand");
516 Parameters[1] = Status;
517 _SEH2_LEAVE;
518 }
519
520 /* Check if we're already attached to a session */
521 Status = SmpAcquirePrivilege(SE_LOAD_DRIVER_PRIVILEGE, &State);
522 if (AttachedSessionId != -1)
523 {
524 /* Detach from it, we should be in no session right now */
525 Status = NtSetSystemInformation(SystemSessionDetach,
526 &AttachedSessionId,
527 sizeof(AttachedSessionId));
528 ASSERT(NT_SUCCESS(Status));
529 AttachedSessionId = -1;
530 }
531 SmpReleasePrivilege(State);
532
533 /* Wait on either CSRSS or Winlogon to die */
534 Status = NtWaitForMultipleObjects(RTL_NUMBER_OF(Handles),
535 Handles,
536 WaitAny,
537 FALSE,
538 NULL);
539 if (Status == STATUS_WAIT_0)
540 {
541 /* CSRSS is dead, get exit code and prepare for the hard error */
542 RtlInitUnicodeString(&DbgString, L"Windows SubSystem");
543 Status = NtQueryInformationProcess(Handles[0],
544 ProcessBasicInformation,
545 &ProcessInfo,
546 sizeof(ProcessInfo),
547 NULL);
548 DPRINT1("SMSS: Windows subsystem terminated when it wasn't supposed to.\n");
549 }
550 else
551 {
552 /* The initial command is dead or we have another failure */
553 RtlInitUnicodeString(&DbgString, L"Windows Logon Process");
554 if (Status == STATUS_WAIT_1)
555 {
556 /* Winlogon.exe got terminated, get its exit code */
557 Status = NtQueryInformationProcess(Handles[1],
558 ProcessBasicInformation,
559 &ProcessInfo,
560 sizeof(ProcessInfo),
561 NULL);
562 }
563 else
564 {
565 /* Something else satisfied our wait, so set the wait status */
566 ProcessInfo.ExitStatus = Status;
567 Status = STATUS_SUCCESS;
568 }
569 DPRINT1("SMSS: Initial command '%wZ' terminated when it wasn't supposed to.\n",
570 &InitialCommand);
571 }
572
573 /* Check if NtQueryInformationProcess was successful */
574 if (NT_SUCCESS(Status))
575 {
576 /* Then we must have a valid exit status in the structure, use it */
577 Parameters[1] = ProcessInfo.ExitStatus;
578 }
579 else
580 {
581 /* We really don't know what happened, so set a generic error */
582 Parameters[1] = STATUS_UNSUCCESSFUL;
583 }
584 }
585 _SEH2_EXCEPT(SmpUnhandledExceptionFilter(_SEH2_GetExceptionInformation()))
586 {
587 /* The filter should never return here */
588 ASSERT(FALSE);
589 }
590 _SEH2_END;
591
592 /* Something in the init loop failed, terminate SMSS */
593 return SmpTerminate(Parameters, 1, RTL_NUMBER_OF(Parameters));
594 }
595
596 /* EOF */