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