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
9 /* INCLUDES *******************************************************************/
15 /* GLOBALS ********************************************************************/
17 UNICODE_STRING SmpSystemRoot
;
18 ULONG AttachedSessionId
= -1;
19 BOOLEAN SmpDebug
, SmpEnableDots
;
21 HANDLE SmpInitialCommandProcessId
;
23 /* FUNCTIONS ******************************************************************/
25 /* GCC's incompetence strikes again */
27 sprintf_nt(IN PCHAR Buffer
,
33 sprintf(Buffer
, Format
, ap
);
39 SmpExecuteImage(IN PUNICODE_STRING FileName
,
40 IN PUNICODE_STRING Directory
,
41 IN PUNICODE_STRING CommandLine
,
44 IN PRTL_USER_PROCESS_INFORMATION ProcessInformation
)
46 PRTL_USER_PROCESS_INFORMATION ProcessInfo
;
48 RTL_USER_PROCESS_INFORMATION LocalProcessInfo
;
49 PRTL_USER_PROCESS_PARAMETERS ProcessParameters
;
51 /* Use the input process information if we have it, otherwise use local */
52 ProcessInfo
= ProcessInformation
;
53 if (!ProcessInfo
) ProcessInfo
= &LocalProcessInfo
;
55 /* Create parameters for the target process */
56 Status
= RtlCreateProcessParameters(&ProcessParameters
,
58 SmpDefaultLibPath
.Length
?
59 &SmpDefaultLibPath
: NULL
,
62 SmpDefaultEnvironment
,
67 if (!NT_SUCCESS(Status
))
69 /* This is a pretty bad failure. ASSERT on checked builds and exit */
70 ASSERTMSG(NT_SUCCESS(Status
), "RtlCreateProcessParameters");
71 DPRINT1("SMSS: RtlCreateProcessParameters failed for %wZ - Status == %lx\n",
76 /* Set the size field as required */
77 ProcessInfo
->Size
= sizeof(RTL_USER_PROCESS_INFORMATION
);
79 /* Check if the debug flag was requested */
80 if (Flags
& SMP_DEBUG_FLAG
)
82 /* Write it in the process parameters */
83 ProcessParameters
->DebugFlags
= 1;
87 /* Otherwise inherit the flag that was passed to SMSS itself */
88 ProcessParameters
->DebugFlags
= SmpDebug
;
91 /* Subsystems get the first 1MB of memory reserved for DOS/IVT purposes */
92 if (Flags
& SMP_SUBSYSTEM_FLAG
)
94 ProcessParameters
->Flags
|= RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB
;
97 /* And always force NX for anything that SMSS launches */
98 ProcessParameters
->Flags
|= RTL_USER_PROCESS_PARAMETERS_NX
;
100 /* Now create the process */
101 Status
= RtlCreateUserProcess(FileName
,
102 OBJ_CASE_INSENSITIVE
,
111 RtlDestroyProcessParameters(ProcessParameters
);
112 if (!NT_SUCCESS(Status
))
114 /* If we couldn't create it, fail back to the caller */
115 DPRINT1("SMSS: Failed load of %wZ - Status == %lx\n",
120 /* Associate a session with this process */
121 Status
= SmpSetProcessMuSessionId(ProcessInfo
->ProcessHandle
, MuSessionId
);
123 /* If the application is deferred (suspended), there's nothing to do */
124 if (Flags
& SMP_DEFERRED_FLAG
) return Status
;
126 /* Otherwise, get ready to start it, but make sure it's a native app */
127 if (ProcessInfo
->ImageInformation
.SubSystemType
== IMAGE_SUBSYSTEM_NATIVE
)
130 NtResumeThread(ProcessInfo
->ThreadHandle
, NULL
);
131 if (!(Flags
& SMP_ASYNC_FLAG
))
133 /* Block on it unless Async was requested */
134 NtWaitForSingleObject(ProcessInfo
->ThreadHandle
, FALSE
, NULL
);
137 /* It's up and running now, close our handles */
138 NtClose(ProcessInfo
->ThreadHandle
);
139 NtClose(ProcessInfo
->ProcessHandle
);
143 /* This image is invalid, so kill it, close our handles, and fail */
144 Status
= STATUS_INVALID_IMAGE_FORMAT
;
145 NtTerminateProcess(ProcessInfo
->ProcessHandle
, Status
);
146 NtWaitForSingleObject(ProcessInfo
->ThreadHandle
, 0, 0);
147 NtClose(ProcessInfo
->ThreadHandle
);
148 NtClose(ProcessInfo
->ProcessHandle
);
149 DPRINT1("SMSS: Not an NT image - %wZ\n", FileName
);
152 /* Return the outcome of the process create */
158 SmpInvokeAutoChk(IN PUNICODE_STRING FileName
,
159 IN PUNICODE_STRING Directory
,
160 IN PUNICODE_STRING Arguments
,
163 ANSI_STRING MessageString
;
164 CHAR MessageBuffer
[256];
165 UNICODE_STRING Destination
;
167 BOOLEAN BootState
, BootOkay
, ShutdownOkay
;
169 /* Check if autochk should show dots (if the user booted with /SOS) */
170 if (SmpQueryRegistrySosOption()) SmpEnableDots
= FALSE
;
172 /* Make sure autochk was actually found */
173 if (Flags
& SMP_INVALID_PATH
)
175 /* It wasn't, so create an error message to print on the screen */
176 sprintf_nt(MessageBuffer
,
177 "%wZ program not found - skipping AUTOCHECK\n",
179 RtlInitAnsiString(&MessageString
, MessageBuffer
);
180 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Destination
,
185 NtDisplayString(&Destination
);
186 RtlFreeUnicodeString(&Destination
);
191 /* Autochk is there, so record the BSD state */
192 BootState
= SmpSaveAndClearBootStatusData(&BootOkay
, &ShutdownOkay
);
194 /* Build the path to autochk and place its arguments */
195 RtlInitEmptyUnicodeString(&Destination
, Buffer
, sizeof(Buffer
));
196 RtlAppendUnicodeStringToString(&Destination
, FileName
);
197 RtlAppendUnicodeToString(&Destination
, L
" ");
198 RtlAppendUnicodeStringToString(&Destination
, Arguments
);
201 SmpExecuteImage(FileName
,
205 Flags
& ~SMP_AUTOCHK_FLAG
,
208 /* Restore the BSD state */
209 if (BootState
) SmpRestoreBootStatusData(BootOkay
, ShutdownOkay
);
212 /* We're all done! */
213 return STATUS_SUCCESS
;
218 SmpExecuteCommand(IN PUNICODE_STRING CommandLine
,
219 IN ULONG MuSessionId
,
220 OUT PHANDLE ProcessId
,
224 UNICODE_STRING Arguments
, Directory
, FileName
;
226 /* There's no longer a debugging subsystem */
227 if (Flags
& SMP_DEBUG_FLAG
) return STATUS_SUCCESS
;
229 /* Parse the command line to see what execution flags are requested */
230 Status
= SmpParseCommandLine(CommandLine
,
235 if (!NT_SUCCESS(Status
))
237 /* Fail if we couldn't do that */
238 DPRINT1("SMSS: SmpParseCommand( %wZ ) failed - Status == %lx\n",
239 CommandLine
, Status
);
243 /* Check if autochk is requested */
244 if (Flags
& SMP_AUTOCHK_FLAG
)
247 Status
= SmpInvokeAutoChk(&FileName
, &Directory
, &Arguments
, Flags
);
249 else if (Flags
& SMP_SUBSYSTEM_FLAG
)
251 Status
= SmpLoadSubSystem(&FileName
,
258 else if (Flags
& SMP_INVALID_PATH
)
260 /* An invalid image was specified, fail */
261 DPRINT1("SMSS: Image file (%wZ) not found\n", &FileName
);
262 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
266 /* An actual image name was present -- execute it */
267 Status
= SmpExecuteImage(&FileName
,
275 /* Free all the token parameters */
276 if (FileName
.Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, FileName
.Buffer
);
277 if (Directory
.Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Directory
.Buffer
);
278 if (Arguments
.Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Arguments
.Buffer
);
280 /* Return to the caller */
281 if (!NT_SUCCESS(Status
))
283 DPRINT1("SMSS: Command '%wZ' failed - Status == %x\n",
284 CommandLine
, Status
);
291 SmpExecuteInitialCommand(IN ULONG MuSessionId
,
292 IN PUNICODE_STRING InitialCommand
,
293 IN HANDLE InitialCommandProcess
,
294 OUT PHANDLE ReturnPid
)
297 RTL_USER_PROCESS_INFORMATION ProcessInfo
;
298 UNICODE_STRING Arguments
, ImageFileDirectory
, ImageFileName
;
301 /* Check if we haven't yet connected to ourselves */
304 /* Connect to ourselves, as a client */
305 Status
= SmConnectToSm(0, 0, 0, &SmApiPort
);
306 if (!NT_SUCCESS(Status
))
308 DPRINT1("SMSS: Unable to connect to SM - Status == %lx\n", Status
);
313 /* Parse the initial command line */
314 Status
= SmpParseCommandLine(InitialCommand
,
319 if (Flags
& SMP_INVALID_PATH
)
321 /* Fail if it doesn't exist */
322 DPRINT1("SMSS: Initial command image (%wZ) not found\n", &ImageFileName
);
323 if (ImageFileName
.Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, ImageFileName
.Buffer
);
324 return STATUS_OBJECT_NAME_NOT_FOUND
;
327 /* And fail if any other reason is also true */
328 if (!NT_SUCCESS(Status
))
330 DPRINT1("SMSS: SmpParseCommand( %wZ ) failed - Status == %lx\n",
331 InitialCommand
, Status
);
335 /* Execute the initial command -- but defer its full execution */
336 Status
= SmpExecuteImage(&ImageFileName
,
343 /* Free any buffers we had lying around */
344 if (ImageFileName
.Buffer
)
346 RtlFreeHeap(RtlGetProcessHeap(), 0, ImageFileName
.Buffer
);
348 if (ImageFileDirectory
.Buffer
)
350 RtlFreeHeap(RtlGetProcessHeap(), 0, ImageFileDirectory
.Buffer
);
352 if (Arguments
.Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Arguments
.Buffer
);
354 /* Bail out if we couldn't execute the initial command */
355 if (!NT_SUCCESS(Status
)) return Status
;
357 /* Now duplicate the handle to this process */
358 Status
= NtDuplicateObject(NtCurrentProcess(),
359 ProcessInfo
.ProcessHandle
,
361 InitialCommandProcess
,
365 if (!NT_SUCCESS(Status
))
367 /* Kill it utterly if duplication failed */
368 DPRINT1("SMSS: DupObject Failed. Status == %lx\n", Status
);
369 NtTerminateProcess(ProcessInfo
.ProcessHandle
, Status
);
370 NtResumeThread(ProcessInfo
.ThreadHandle
, NULL
);
371 NtClose(ProcessInfo
.ThreadHandle
);
372 NtClose(ProcessInfo
.ProcessHandle
);
376 /* Return PID to the caller, and set this as the initial command PID */
377 if (ReturnPid
) *ReturnPid
= ProcessInfo
.ClientId
.UniqueProcess
;
378 if (!MuSessionId
) SmpInitialCommandProcessId
= ProcessInfo
.ClientId
.UniqueProcess
;
380 /* Now call our server execution function to wrap up its initialization */
381 Status
= SmExecPgm(SmApiPort
, &ProcessInfo
, FALSE
);
382 if (!NT_SUCCESS(Status
)) DPRINT1("SMSS: SmExecPgm Failed. Status == %lx\n", Status
);
388 SmpTerminate(IN PULONG_PTR Parameters
,
389 IN ULONG ParameterMask
,
390 IN ULONG ParameterCount
)
396 /* Give the shutdown privilege to the thread */
397 if (RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE
, TRUE
, TRUE
, &Old
) ==
400 /* Thread doesn't have a token, give it to the entire process */
401 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE
, TRUE
, FALSE
, &Old
);
404 /* Take down the process/machine with a hard error */
405 Status
= NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED
,
409 OptionShutdownSystem
,
412 /* Terminate the process if the hard error didn't already */
413 return NtTerminateProcess(NtCurrentProcess(), Status
);
417 SmpUnhandledExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo
)
419 ULONG_PTR Parameters
[4];
420 UNICODE_STRING DestinationString
;
422 /* Print and breakpoint into the debugger */
423 DbgPrint("SMSS: Unhandled exception - Status == %x IP == %x\n",
424 ExceptionInfo
->ExceptionRecord
->ExceptionCode
,
425 ExceptionInfo
->ExceptionRecord
->ExceptionAddress
);
426 DbgPrint(" Memory Address: %x Read/Write: %x\n",
427 ExceptionInfo
->ExceptionRecord
->ExceptionInformation
[0],
428 ExceptionInfo
->ExceptionRecord
->ExceptionInformation
[1]);
431 /* Build the hard error and terminate */
432 RtlInitUnicodeString(&DestinationString
, L
"Unhandled Exception in Session Manager");
433 Parameters
[0] = (ULONG_PTR
)&DestinationString
;
434 Parameters
[1] = ExceptionInfo
->ExceptionRecord
->ExceptionCode
;
435 Parameters
[2] = (ULONG_PTR
)ExceptionInfo
->ExceptionRecord
->ExceptionAddress
;
436 Parameters
[3] = (ULONG_PTR
)ExceptionInfo
->ContextRecord
;
437 SmpTerminate(Parameters
, 1, RTL_NUMBER_OF(Parameters
));
439 /* We hould never get here */
441 return EXCEPTION_EXECUTE_HANDLER
;
451 KPRIORITY SetBasePriority
;
452 ULONG_PTR Parameters
[4];
456 PROCESS_BASIC_INFORMATION ProcessInfo
;
457 UNICODE_STRING DbgString
, InitialCommand
;
459 /* Make us critical */
460 RtlSetProcessIsCritical(TRUE
, NULL
, FALSE
);
461 RtlSetThreadIsCritical(TRUE
, NULL
, FALSE
);
463 /* Raise our priority */
464 SetBasePriority
= 11;
465 Status
= NtSetInformationProcess(NtCurrentProcess(),
467 (PVOID
)&SetBasePriority
,
468 sizeof(SetBasePriority
));
469 ASSERT(NT_SUCCESS(Status
));
471 /* Save the debug flag if it was passed */
472 if (DebugFlag
) SmpDebug
= DebugFlag
!= 0;
474 /* Build the hard error parameters */
475 Parameters
[0] = (ULONG_PTR
)&DbgString
;
476 Parameters
[1] = Parameters
[2] = Parameters
[3] = 0;
478 /* Enter SEH so we can terminate correctly if anything goes wrong */
481 /* Initialize SMSS */
482 Status
= SmpInit(&InitialCommand
, Handles
);
483 if (!NT_SUCCESS(Status
))
485 DPRINT1("SMSS: SmpInit return failure - Status == %x\n", Status
);
486 RtlInitUnicodeString(&DbgString
, L
"Session Manager Initialization");
487 Parameters
[1] = Status
;
491 /* Get the global flags */
492 Status
= NtQuerySystemInformation(SystemFlagsInformation
,
496 ASSERT(NT_SUCCESS(Status
));
498 /* Before executing the initial command check if the debug flag is on */
499 if (Flags
& (FLG_DEBUG_INITIAL_COMMAND
| FLG_DEBUG_INITIAL_COMMAND_EX
))
501 /* SMSS should launch ntsd with a few parameters at this point */
502 DPRINT1("Global Flags Set to SMSS Debugging: Not yet supported\n");
505 /* Execute the initial command (Winlogon.exe) */
506 Status
= SmpExecuteInitialCommand(0, &InitialCommand
, &Handles
[1], NULL
);
507 if (!NT_SUCCESS(Status
))
509 /* Fail and raise a hard error */
510 DPRINT1("SMSS: Execute Initial Command failed\n");
511 RtlInitUnicodeString(&DbgString
,
512 L
"Session Manager ExecuteInitialCommand");
513 Parameters
[1] = Status
;
517 /* Check if we're already attached to a session */
518 Status
= SmpAcquirePrivilege(SE_LOAD_DRIVER_PRIVILEGE
, &State
);
519 if (AttachedSessionId
!= -1)
521 /* Detach from it, we should be in no session right now */
522 Status
= NtSetSystemInformation(SystemSessionDetach
,
524 sizeof(AttachedSessionId
));
525 ASSERT(NT_SUCCESS(Status
));
526 AttachedSessionId
= -1;
528 SmpReleasePrivilege(State
);
530 /* Wait on either CSRSS or Winlogon to die */
531 Status
= NtWaitForMultipleObjects(RTL_NUMBER_OF(Handles
),
536 if (Status
== STATUS_WAIT_0
)
538 /* CSRSS is dead, get exit code and prepare for the hard error */
539 RtlInitUnicodeString(&DbgString
, L
"Windows SubSystem");
540 Status
= NtQueryInformationProcess(Handles
[0],
541 ProcessBasicInformation
,
545 DPRINT1("SMSS: Windows subsystem terminated when it wasn't supposed to.\n");
549 /* The initial command is dead or we have another failure */
550 RtlInitUnicodeString(&DbgString
, L
"Windows Logon Process");
551 if (Status
== STATUS_WAIT_1
)
553 /* Winlogon.exe got terminated, get its exit code */
554 Status
= NtQueryInformationProcess(Handles
[1],
555 ProcessBasicInformation
,
562 /* Something else satisfied our wait, so set the wait status */
563 ProcessInfo
.ExitStatus
= Status
;
564 Status
= STATUS_SUCCESS
;
566 DPRINT1("SMSS: Initial command '%wZ' terminated when it wasn't supposed to.\n",
570 /* Check if NtQueryInformationProcess was successful */
571 if (NT_SUCCESS(Status
))
573 /* Then we must have a valid exit status in the structure, use it */
574 Parameters
[1] = ProcessInfo
.ExitStatus
;
578 /* We really don't know what happened, so set a generic error */
579 Parameters
[1] = STATUS_UNSUCCESSFUL
;
582 _SEH2_EXCEPT(SmpUnhandledExceptionFilter(_SEH2_GetExceptionInformation()))
584 /* The filter should never return here */
589 /* Something in the init loop failed, terminate SMSS */
590 return SmpTerminate(Parameters
, 1, RTL_NUMBER_OF(Parameters
));