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 *******************************************************************/
13 #include <pseh/pseh2.h>
18 /* GLOBALS ********************************************************************/
20 UNICODE_STRING SmpSystemRoot
;
21 ULONG AttachedSessionId
= -1;
22 BOOLEAN SmpDebug
, SmpEnableDots
;
24 HANDLE SmpInitialCommandProcessId
;
26 /* FUNCTIONS ******************************************************************/
28 /* GCC's incompetence strikes again */
30 sprintf_nt(IN PCHAR Buffer
,
36 sprintf(Buffer
, Format
, ap
);
42 SmpExecuteImage(IN PUNICODE_STRING FileName
,
43 IN PUNICODE_STRING Directory
,
44 IN PUNICODE_STRING CommandLine
,
47 IN PRTL_USER_PROCESS_INFORMATION ProcessInformation
)
49 PRTL_USER_PROCESS_INFORMATION ProcessInfo
;
51 RTL_USER_PROCESS_INFORMATION LocalProcessInfo
;
52 PRTL_USER_PROCESS_PARAMETERS ProcessParameters
;
54 /* Use the input process information if we have it, otherwise use local */
55 ProcessInfo
= ProcessInformation
;
56 if (!ProcessInfo
) ProcessInfo
= &LocalProcessInfo
;
58 /* Create parameters for the target process */
59 Status
= RtlCreateProcessParameters(&ProcessParameters
,
61 SmpDefaultLibPath
.Length
?
62 &SmpDefaultLibPath
: NULL
,
65 SmpDefaultEnvironment
,
70 if (!NT_SUCCESS(Status
))
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",
79 /* Set the size field as required */
80 ProcessInfo
->Size
= sizeof(RTL_USER_PROCESS_INFORMATION
);
82 /* Check if the debug flag was requested */
83 if (Flags
& SMP_DEBUG_FLAG
)
85 /* Write it in the process parameters */
86 ProcessParameters
->DebugFlags
= 1;
90 /* Otherwise inherit the flag that was passed to SMSS itself */
91 ProcessParameters
->DebugFlags
= SmpDebug
;
94 /* Subsystems get the first 1MB of memory reserved for DOS/IVT purposes */
95 if (Flags
& SMP_SUBSYSTEM_FLAG
)
97 ProcessParameters
->Flags
|= RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB
;
100 /* And always force NX for anything that SMSS launches */
101 ProcessParameters
->Flags
|= RTL_USER_PROCESS_PARAMETERS_NX
;
103 /* Now create the process */
104 Status
= RtlCreateUserProcess(FileName
,
105 OBJ_CASE_INSENSITIVE
,
114 RtlDestroyProcessParameters(ProcessParameters
);
115 if (!NT_SUCCESS(Status
))
117 /* If we couldn't create it, fail back to the caller */
118 DPRINT1("SMSS: Failed load of %wZ - Status == %lx\n",
123 /* Associate a session with this process */
124 Status
= SmpSetProcessMuSessionId(ProcessInfo
->ProcessHandle
, MuSessionId
);
126 /* If the application is deferred (suspended), there's nothing to do */
127 if (Flags
& SMP_DEFERRED_FLAG
) return Status
;
129 /* Otherwise, get ready to start it, but make sure it's a native app */
130 if (ProcessInfo
->ImageInformation
.SubSystemType
== IMAGE_SUBSYSTEM_NATIVE
)
133 NtResumeThread(ProcessInfo
->ThreadHandle
, NULL
);
134 if (!(Flags
& SMP_ASYNC_FLAG
))
136 /* Block on it unless Async was requested */
137 NtWaitForSingleObject(ProcessInfo
->ThreadHandle
, FALSE
, NULL
);
140 /* It's up and running now, close our handles */
141 NtClose(ProcessInfo
->ThreadHandle
);
142 NtClose(ProcessInfo
->ProcessHandle
);
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
);
155 /* Return the outcome of the process create */
161 SmpInvokeAutoChk(IN PUNICODE_STRING FileName
,
162 IN PUNICODE_STRING Directory
,
163 IN PUNICODE_STRING Arguments
,
166 ANSI_STRING MessageString
;
167 CHAR MessageBuffer
[256];
168 UNICODE_STRING Destination
;
170 BOOLEAN BootState
, BootOkay
, ShutdownOkay
;
172 /* Check if autochk should show dots (if the user booted with /SOS) */
173 if (SmpQueryRegistrySosOption()) SmpEnableDots
= FALSE
;
175 /* Make sure autochk was actually found */
176 if (Flags
& SMP_INVALID_PATH
)
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",
182 RtlInitAnsiString(&MessageString
, MessageBuffer
);
183 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Destination
,
188 NtDisplayString(&Destination
);
189 RtlFreeUnicodeString(&Destination
);
194 /* Autochk is there, so record the BSD state */
195 BootState
= SmpSaveAndClearBootStatusData(&BootOkay
, &ShutdownOkay
);
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
);
204 SmpExecuteImage(FileName
,
208 Flags
& ~SMP_AUTOCHK_FLAG
,
211 /* Restore the BSD state */
212 if (BootState
) SmpRestoreBootStatusData(BootOkay
, ShutdownOkay
);
215 /* We're all done! */
216 return STATUS_SUCCESS
;
221 SmpExecuteCommand(IN PUNICODE_STRING CommandLine
,
222 IN ULONG MuSessionId
,
223 OUT PHANDLE ProcessId
,
227 UNICODE_STRING Arguments
, Directory
, FileName
;
229 /* There's no longer a debugging subsystem */
230 if (Flags
& SMP_DEBUG_FLAG
) return STATUS_SUCCESS
;
232 /* Parse the command line to see what execution flags are requested */
233 Status
= SmpParseCommandLine(CommandLine
,
238 if (!NT_SUCCESS(Status
))
240 /* Fail if we couldn't do that */
241 DPRINT1("SMSS: SmpParseCommandLine( %wZ ) failed - Status == %lx\n",
242 CommandLine
, Status
);
246 /* Check if autochk is requested */
247 if (Flags
& SMP_AUTOCHK_FLAG
)
250 Status
= SmpInvokeAutoChk(&FileName
, &Directory
, &Arguments
, Flags
);
252 else if (Flags
& SMP_SUBSYSTEM_FLAG
)
254 Status
= SmpLoadSubSystem(&FileName
,
261 else if (Flags
& SMP_INVALID_PATH
)
263 /* An invalid image was specified, fail */
264 DPRINT1("SMSS: Image file (%wZ) not found\n", &FileName
);
265 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
269 /* An actual image name was present -- execute it */
270 Status
= SmpExecuteImage(&FileName
,
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
);
283 /* Return to the caller */
284 if (!NT_SUCCESS(Status
))
286 DPRINT1("SMSS: Command '%wZ' failed - Status == %x\n",
287 CommandLine
, Status
);
294 SmpExecuteInitialCommand(IN ULONG MuSessionId
,
295 IN PUNICODE_STRING InitialCommand
,
296 IN HANDLE InitialCommandProcess
,
297 OUT PHANDLE ReturnPid
)
300 RTL_USER_PROCESS_INFORMATION ProcessInfo
;
301 UNICODE_STRING Arguments
, ImageFileDirectory
, ImageFileName
;
304 /* Check if we haven't yet connected to ourselves */
307 /* Connect to ourselves, as a client */
308 Status
= SmConnectToSm(0, 0, 0, &SmApiPort
);
309 if (!NT_SUCCESS(Status
))
311 DPRINT1("SMSS: Unable to connect to SM - Status == %lx\n", Status
);
316 /* Parse the initial command line */
317 Status
= SmpParseCommandLine(InitialCommand
,
322 if (Flags
& SMP_INVALID_PATH
)
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
;
330 /* And fail if any other reason is also true */
331 if (!NT_SUCCESS(Status
))
333 DPRINT1("SMSS: SmpParseCommandLine( %wZ ) failed - Status == %lx\n",
334 InitialCommand
, Status
);
338 /* Execute the initial command -- but defer its full execution */
339 Status
= SmpExecuteImage(&ImageFileName
,
346 /* Free any buffers we had lying around */
347 if (ImageFileName
.Buffer
)
349 RtlFreeHeap(RtlGetProcessHeap(), 0, ImageFileName
.Buffer
);
351 if (ImageFileDirectory
.Buffer
)
353 RtlFreeHeap(RtlGetProcessHeap(), 0, ImageFileDirectory
.Buffer
);
355 if (Arguments
.Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Arguments
.Buffer
);
357 /* Bail out if we couldn't execute the initial command */
358 if (!NT_SUCCESS(Status
)) return Status
;
360 /* Now duplicate the handle to this process */
361 Status
= NtDuplicateObject(NtCurrentProcess(),
362 ProcessInfo
.ProcessHandle
,
364 InitialCommandProcess
,
368 if (!NT_SUCCESS(Status
))
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
);
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
;
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
);
391 SmpTerminate(IN PULONG_PTR Parameters
,
392 IN ULONG ParameterMask
,
393 IN ULONG ParameterCount
)
399 /* Give the shutdown privilege to the thread */
400 if (RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE
, TRUE
, TRUE
, &Old
) ==
403 /* Thread doesn't have a token, give it to the entire process */
404 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE
, TRUE
, FALSE
, &Old
);
407 /* Take down the process/machine with a hard error */
408 Status
= NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED
,
412 OptionShutdownSystem
,
415 /* Terminate the process if the hard error didn't already */
416 return NtTerminateProcess(NtCurrentProcess(), Status
);
420 SmpUnhandledExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo
)
422 ULONG_PTR Parameters
[4];
423 UNICODE_STRING DestinationString
;
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]);
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
));
442 /* We hould never get here */
444 return EXCEPTION_EXECUTE_HANDLER
;
454 KPRIORITY SetBasePriority
;
455 ULONG_PTR Parameters
[4];
459 PROCESS_BASIC_INFORMATION ProcessInfo
;
460 UNICODE_STRING DbgString
, InitialCommand
;
462 /* Make us critical */
463 RtlSetProcessIsCritical(TRUE
, NULL
, FALSE
);
464 RtlSetThreadIsCritical(TRUE
, NULL
, FALSE
);
466 /* Raise our priority */
467 SetBasePriority
= 11;
468 Status
= NtSetInformationProcess(NtCurrentProcess(),
470 (PVOID
)&SetBasePriority
,
471 sizeof(SetBasePriority
));
472 ASSERT(NT_SUCCESS(Status
));
474 /* Save the debug flag if it was passed */
475 if (DebugFlag
) SmpDebug
= DebugFlag
!= 0;
477 /* Build the hard error parameters */
478 Parameters
[0] = (ULONG_PTR
)&DbgString
;
479 Parameters
[1] = Parameters
[2] = Parameters
[3] = 0;
481 /* Enter SEH so we can terminate correctly if anything goes wrong */
484 /* Initialize SMSS */
485 Status
= SmpInit(&InitialCommand
, Handles
);
486 if (!NT_SUCCESS(Status
))
488 DPRINT1("SMSS: SmpInit return failure - Status == %x\n", Status
);
489 RtlInitUnicodeString(&DbgString
, L
"Session Manager Initialization");
490 Parameters
[1] = Status
;
494 /* Get the global flags */
495 Status
= NtQuerySystemInformation(SystemFlagsInformation
,
499 ASSERT(NT_SUCCESS(Status
));
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
))
504 /* SMSS should launch ntsd with a few parameters at this point */
505 DPRINT1("Global Flags Set to SMSS Debugging: Not yet supported\n");
508 /* Execute the initial command (Winlogon.exe) */
509 Status
= SmpExecuteInitialCommand(0, &InitialCommand
, &Handles
[1], NULL
);
510 if (!NT_SUCCESS(Status
))
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
;
520 /* Check if we're already attached to a session */
521 Status
= SmpAcquirePrivilege(SE_LOAD_DRIVER_PRIVILEGE
, &State
);
522 if (AttachedSessionId
!= -1)
524 /* Detach from it, we should be in no session right now */
525 Status
= NtSetSystemInformation(SystemSessionDetach
,
527 sizeof(AttachedSessionId
));
528 ASSERT(NT_SUCCESS(Status
));
529 AttachedSessionId
= -1;
531 SmpReleasePrivilege(State
);
533 /* Wait on either CSRSS or Winlogon to die */
534 Status
= NtWaitForMultipleObjects(RTL_NUMBER_OF(Handles
),
539 if (Status
== STATUS_WAIT_0
)
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
,
548 DPRINT1("SMSS: Windows subsystem terminated when it wasn't supposed to.\n");
552 /* The initial command is dead or we have another failure */
553 RtlInitUnicodeString(&DbgString
, L
"Windows Logon Process");
554 if (Status
== STATUS_WAIT_1
)
556 /* Winlogon.exe got terminated, get its exit code */
557 Status
= NtQueryInformationProcess(Handles
[1],
558 ProcessBasicInformation
,
565 /* Something else satisfied our wait, so set the wait status */
566 ProcessInfo
.ExitStatus
= Status
;
567 Status
= STATUS_SUCCESS
;
569 DPRINT1("SMSS: Initial command '%wZ' terminated when it wasn't supposed to.\n",
573 /* Check if NtQueryInformationProcess was successful */
574 if (NT_SUCCESS(Status
))
576 /* Then we must have a valid exit status in the structure, use it */
577 Parameters
[1] = ProcessInfo
.ExitStatus
;
581 /* We really don't know what happened, so set a generic error */
582 Parameters
[1] = STATUS_UNSUCCESSFUL
;
585 _SEH2_EXCEPT(SmpUnhandledExceptionFilter(_SEH2_GetExceptionInformation()))
587 /* The filter should never return here */
592 /* Something in the init loop failed, terminate SMSS */
593 return SmpTerminate(Parameters
, 1, RTL_NUMBER_OF(Parameters
));