From 14ba04899c8d26ab6fd94023165afa903e6f1feb Mon Sep 17 00:00:00 2001 From: Alex Ionescu Date: Thu, 9 Feb 2012 02:21:56 +0000 Subject: [PATCH] [SMSS2]: Implement SmpCallCsrCreateProcess and SmpLoadSubsystem. We now load CSRSS and everything works 100%! Winlogon is now launched by SMSS2 and no regressions have been found. [SMSS]: Turn off all code other than setting up the pagefile. There's a lot of debug prints, as soon as things seem stable after a few days, SMSS will be gone and SMSS2 will take over and DPRINT1s will mostly be gone. svn path=/trunk/; revision=55509 --- reactos/base/system/smss/init.c | 8 +- reactos/base/system/smss2/smsubsys.c | 394 ++++++++++++++++++++++++++- 2 files changed, 395 insertions(+), 7 deletions(-) diff --git a/reactos/base/system/smss/init.c b/reactos/base/system/smss/init.c index 90de9328cde..7a94022fe27 100644 --- a/reactos/base/system/smss/init.c +++ b/reactos/base/system/smss/init.c @@ -24,8 +24,8 @@ struct { } InitRoutine [] = { {TRUE, SmCreateHeap, "create private heap, aborting"}, // {TRUE, SmCreateObjectDirectories, "create object directories"}, - {TRUE, SmCreateApiPort, "create \\SmApiPort"}, - {TRUE, SmCreateEnvironment, "create the system environment"}, +// {TRUE, SmCreateApiPort, "create \\SmApiPort"}, +// {TRUE, SmCreateEnvironment, "create the system environment"}, // {TRUE, SmSetEnvironmentVariables, "set system environment variables"}, // {TRUE, SmInitDosDevices, "create dos device links"}, // {TRUE, SmRunBootApplications, "run boot applications"}, @@ -34,8 +34,8 @@ struct { // {FALSE, SmLoadKnownDlls, "preload system DLLs"}, {TRUE, SmCreatePagingFiles, "create paging files"}, // {TRUE, SmInitializeRegistry, "initialize the registry"}, - {TRUE, SmInitializeClientManagement, "initialize client management"}, - {TRUE, SmLoadSubsystems, "load subsystems"} +// {TRUE, SmInitializeClientManagement, "initialize client management"}, +// {TRUE, SmLoadSubsystems, "load subsystems"} }; NTSTATUS diff --git a/reactos/base/system/smss2/smsubsys.c b/reactos/base/system/smss2/smsubsys.c index d38695c6bb6..d0bfbd83b9c 100644 --- a/reactos/base/system/smss2/smsubsys.c +++ b/reactos/base/system/smss2/smsubsys.c @@ -23,6 +23,24 @@ WCHAR InitialCommandBuffer[256]; /* FUNCTIONS ******************************************************************/ +NTSTATUS +NTAPI +SmpCallCsrCreateProcess(IN PSB_API_MSG SbApiMsg, + IN USHORT MessageLength, + IN HANDLE PortHandle) +{ + NTSTATUS Status; + + /* Initialize the header and send the message to CSRSS */ + SbApiMsg->h.u2.ZeroInit = 0; + SbApiMsg->h.u1.s1.DataLength = MessageLength + 8; + SbApiMsg->h.u1.s1.TotalLength = sizeof(SB_API_MSG); + SbApiMsg->ApiNumber = SbpCreateProcess; + Status = NtRequestWaitReplyPort(PortHandle, &SbApiMsg->h, &SbApiMsg->h); + if (NT_SUCCESS(Status)) Status = SbApiMsg->ReturnValue; + return Status; +} + VOID NTAPI SmpDereferenceSubsystem(IN PSMP_SUBSYSTEM SubSystem) @@ -60,7 +78,7 @@ SmpLocateKnownSubSysByCid(IN PCLIENT_ID ClientId) { /* Check if this one matches the client ID and is still valid */ Subsystem = CONTAINING_RECORD(NextEntry, SMP_SUBSYSTEM, Entry); - if ((*(PULONGLONG)&Subsystem->ClientId == *(PULONGLONG)&ClientId) && + if ((*(PULONGLONG)&Subsystem->ClientId == *(PULONGLONG)ClientId) && !(Subsystem->Terminating)) { /* Add a reference and return it */ @@ -123,8 +141,378 @@ SmpLoadSubSystem(IN PUNICODE_STRING FileName, OUT PHANDLE ProcessId, IN ULONG Flags) { - DPRINT1("Should start subsystem %wZ for Session: %lx\n", FileName, MuSessionId); - return STATUS_SUCCESS; + PSMP_SUBSYSTEM Subsystem, NewSubsystem, KnownSubsystem = NULL; + HANDLE SubSysProcessId; + NTSTATUS Status = STATUS_SUCCESS; + SB_API_MSG SbApiMsg, SbApiMsg2; + RTL_USER_PROCESS_INFORMATION ProcessInformation; + LARGE_INTEGER Timeout; + PVOID State; + PSB_CREATE_PROCESS_MSG CreateProcess = &SbApiMsg.CreateProcess; + PSB_CREATE_SESSION_MSG CreateSession = &SbApiMsg.CreateSession; + DPRINT1("Loading subsystem: %wZ\n", FileName); + + /* Make sure this is a found subsystem */ + if (Flags & SMP_INVALID_PATH) + { + DPRINT1("SMSS: Unable to find subsystem - %wZ\n", FileName); + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + /* Don't use a session if the flag is set */ + if (Flags & 0x80) MuSessionId = 0; + + /* Lock the subsystems while we do a look up */ + RtlEnterCriticalSection(&SmpKnownSubSysLock); + while (TRUE) + { + /* Check if we found a subsystem not yet fully iniitalized */ + Subsystem = SmpLocateKnownSubSysByType(MuSessionId, -1); + if (!Subsystem) break; + RtlLeaveCriticalSection(&SmpKnownSubSysLock); + + /* Wait on it to initialize */ + NtWaitForSingleObject(Subsystem->Event, FALSE, NULL); + + /* Dereference it and try the next one */ + RtlEnterCriticalSection(&SmpKnownSubSysLock); + SmpDereferenceSubsystem(Subsystem); + } + + /* Check if this is a POSIX subsystem */ + if (Flags & SMP_POSIX_FLAG) + { + /* Do we already have it? */ + Subsystem = SmpLocateKnownSubSysByType(MuSessionId, IMAGE_SUBSYSTEM_POSIX_CUI); + } + else if (Flags & SMP_OS2_FLAG) + { + /* This is an OS/2 subsystem, do we we already have it? */ + Subsystem = SmpLocateKnownSubSysByType(MuSessionId, IMAGE_SUBSYSTEM_OS2_CUI); + } + + /* Check if we already have one of the optional subsystems for the session */ + if (Subsystem) + { + /* Dereference and return, no work to do */ + SmpDereferenceSubsystem(Subsystem); + RtlLeaveCriticalSection(&SmpKnownSubSysLock); + return STATUS_SUCCESS; + } + + /* Allocate a new subsystem! */ + NewSubsystem = RtlAllocateHeap(SmpHeap, SmBaseTag, sizeof(SMP_SUBSYSTEM)); + DPRINT1("new subsystem created: %p\n", NewSubsystem); + if (!NewSubsystem) + { + RtlLeaveCriticalSection(&SmpKnownSubSysLock); + return STATUS_NO_MEMORY; + } + + /* Initialize its header and reference count */ + NewSubsystem->ReferenceCount = 1; + NewSubsystem->MuSessionId = MuSessionId; + NewSubsystem->ImageType = -1; + + /* Clear out all the other data for now */ + NewSubsystem->Terminating = FALSE; + NewSubsystem->ProcessHandle = NULL; + NewSubsystem->Event = NULL; + NewSubsystem->PortHandle = NULL; + NewSubsystem->SbApiPort = NULL; + + /* Create the event we'll be wating on for initialization */ + Status = NtCreateEvent(&NewSubsystem->Event, + EVENT_ALL_ACCESS, + NULL, + NotificationEvent, + FALSE); + if (!NT_SUCCESS(Status)) + { + /* This failed, bail out */ + RtlFreeHeap(SmpHeap, 0, NewSubsystem); + RtlLeaveCriticalSection(&SmpKnownSubSysLock); + return STATUS_NO_MEMORY; + } + + /* Insert the subsystem and release the lock. It can now be found */ + InsertTailList(&SmpKnownSubSysHead, &NewSubsystem->Entry); + RtlLeaveCriticalSection(&SmpKnownSubSysLock); + + /* The OS/2 and POSIX subsystems are actually Windows applications! */ + if (Flags & (SMP_POSIX_FLAG | SMP_OS2_FLAG)) + { + /* Locate the Windows subsystem for this session */ + KnownSubsystem = SmpLocateKnownSubSysByType(MuSessionId, + IMAGE_SUBSYSTEM_WINDOWS_GUI); + if (!KnownSubsystem) + { + DPRINT1("SMSS: SmpLoadSubSystem - SmpLocateKnownSubSysByType Failed\n"); + goto Quickie2; + } + + /* Fill out all the process details and call CSRSS to launch it */ + CreateProcess->In.ImageName = FileName; + CreateProcess->In.CurrentDirectory = Directory; + CreateProcess->In.CommandLine = CommandLine; + CreateProcess->In.DllPath = SmpDefaultLibPath.Length ? + &SmpDefaultLibPath : NULL; + CreateProcess->In.Flags = Flags | SMP_DEFERRED_FLAG; + CreateProcess->In.DebugFlags = SmpDebug; + Status = SmpCallCsrCreateProcess(&SbApiMsg, + sizeof(*CreateProcess), + KnownSubsystem->SbApiPort); + if (!NT_SUCCESS(Status)) + { + /* Handle failures */ + DPRINT1("SMSS: SmpLoadSubSystem - SmpCallCsrCreateProcess Failed with Status %lx\n", + Status); + goto Quickie2; + } + + /* Save the process information we'll need for the create session */ + ProcessInformation.ProcessHandle = CreateProcess->Out.ProcessHandle; + ProcessInformation.ThreadHandle = CreateProcess->Out.ThreadHandle; + ProcessInformation.ClientId = CreateProcess->Out.ClientId; + ProcessInformation.ImageInformation.SubSystemType = CreateProcess->Out.SubsystemType; + } + else + { + /* This must be CSRSS itself, so just launch it and that's it */ + DPRINT1("Executing CSRSS\n"); + Status = SmpExecuteImage(FileName, + Directory, + CommandLine, + MuSessionId, + Flags | SMP_DEFERRED_FLAG, + &ProcessInformation); + if (!NT_SUCCESS(Status)) + { + /* Handle failures */ + DPRINT1("SMSS: SmpLoadSubSystem - SmpExecuteImage Failed with Status %lx\n", + Status); + goto Quickie2; + } + } + + /* Fill out the handle and client ID in the subsystem structure now */ + NewSubsystem->ProcessHandle = ProcessInformation.ProcessHandle; + NewSubsystem->ClientId = ProcessInformation.ClientId; + + /* Check if we launched a native image or a subsystem-backed image */ + if (ProcessInformation.ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_NATIVE) + { + /* This must be CSRSS itself, since it's a native subsystem image */ + SubSysProcessId = ProcessInformation.ClientId.UniqueProcess; + if ((ProcessId) && !(*ProcessId)) *ProcessId = SubSysProcessId; + + /* Was this the initial CSRSS on Session 0? */ + if (!MuSessionId) + { + /* Then save it in the global variables */ + SmpWindowsSubSysProcessId = SubSysProcessId; + SmpWindowsSubSysProcess = ProcessInformation.ProcessHandle; + } + ASSERT(NT_SUCCESS(Status)); + DPRINT1("CSRSS is up and running: %lx %lx\n", SmpWindowsSubSysProcessId, SmpWindowsSubSysProcess); + } + else + { + /* This is the POSIX or OS/2 subsystem process, copy its information */ + RtlCopyMemory(&CreateSession->ProcessInfo, + &ProcessInformation, + sizeof(&CreateSession->ProcessInfo)); + + /* Not sure these field mean what I think they do -- but clear them */ + *(PULONGLONG)&CreateSession->ClientId = 0; + CreateSession->MuSessionId = 0; + + /* This should find CSRSS because they are POSIX or OS/2 subsystems */ + Subsystem = SmpLocateKnownSubSysByType(MuSessionId, + ProcessInformation.ImageInformation.SubSystemType); + if (!Subsystem) + { + /* Odd failure -- but handle it anyway */ + Status = STATUS_NO_SUCH_PACKAGE; + DPRINT1("SMSS: SmpLoadSubSystem - SmpLocateKnownSubSysByType Failed with Status %lx for sessionid %ld\n", + Status, + MuSessionId); + goto Quickie; + } + + /* Duplicate the parent process handle for the subsystem to have */ + Status = NtDuplicateObject(NtCurrentProcess(), + ProcessInformation.ProcessHandle, + Subsystem->ProcessHandle, + &CreateSession->ProcessInfo.ProcessHandle, + PROCESS_ALL_ACCESS, + 0, + 0); + if (!NT_SUCCESS(Status)) + { + /* Fail since this is critical */ + DPRINT1("SMSS: SmpLoadSubSystem - NtDuplicateObject Failed with Status %lx for sessionid %ld\n", + Status, + MuSessionId); + goto Quickie; + } + + /* Duplicate the initial thread handle for the subsystem to have */ + Status = NtDuplicateObject(NtCurrentProcess(), + ProcessInformation.ThreadHandle, + Subsystem->ProcessHandle, + &CreateSession->ProcessInfo.ThreadHandle, + THREAD_ALL_ACCESS, + 0, + 0); + if (!NT_SUCCESS(Status)) + { + /* Fail since this is critical */ + DPRINT1("SMSS: SmpLoadSubSystem - NtDuplicateObject Failed with Status %lx for sessionid %ld\n", + Status, + MuSessionId); + goto Quickie; + } + + /* Allocate an internal Session ID for this subsystem */ + MuSessionId = SmpAllocateSessionId(Subsystem, 0); + CreateSession->SessionId = MuSessionId; + + /* Send the create session message to the subsystem */ + SbApiMsg2.ReturnValue = STATUS_SUCCESS; + SbApiMsg2.h.u2.ZeroInit = 0; + SbApiMsg2.h.u1.s1.DataLength = sizeof(SB_CREATE_SESSION_MSG) + 8; + SbApiMsg2.h.u1.s1.TotalLength = sizeof(SB_API_MSG); + Status = NtRequestWaitReplyPort(Subsystem->SbApiPort, + &SbApiMsg2.h, + &SbApiMsg2.h); + if (NT_SUCCESS(Status)) Status = SbApiMsg2.ReturnValue; + if (!NT_SUCCESS(Status)) + { + /* Delete the session and handle failure if the LPC call failed */ + SmpDeleteSession(CreateSession->SessionId); + DPRINT1("SMSS: SmpLoadSubSystem - NtRequestWaitReplyPort Failed with Status %lx for sessionid %ld\n", + Status, + CreateSession->SessionId); + goto Quickie; + } + } + + /* Okay, everything looks good to go, initialize this subsystem now! */ + DPRINT1("Resuming the subsystem!\n"); + Status = NtResumeThread(ProcessInformation.ThreadHandle, NULL); + if (!NT_SUCCESS(Status)) + { + /* That didn't work -- back out of everything */ + DPRINT1("SMSS: SmpLoadSubSystem - NtResumeThread failed Status %lx\n", Status); + goto Quickie; + } + + /* Check if this was the subsystem for a different session */ + if (MuSessionId) + { + /* Wait up to 60 seconds for it to initialize */ + Timeout.QuadPart = -600000000; + Status = NtWaitForSingleObject(NewSubsystem->Event, FALSE, &Timeout); + + /* Timeout is done -- does this session still exist? */ + if (!SmpCheckDuplicateMuSessionId(MuSessionId)) + { + /* Nope, it died. Cleanup should've ocurred in a different path. */ + DPRINT1("SMSS: SmpLoadSubSystem - session deleted\n"); + return STATUS_DELETE_PENDING; + } + + /* Check if we timed our or there was another error with the wait */ + if (Status != STATUS_WAIT_0) + { + /* Something is wrong with the subsystem, so back out of everything */ + DPRINT1("SMSS: SmpLoadSubSystem - Timeout waiting for subsystem connect with Status %lx for sessionid %ld\n", + Status, + MuSessionId); + goto Quickie; + } + } + else + { + /* This a session 0 subsystem, just wait for it to initialize */ + DPRINT1("Waiting on the subsystem to initialize...\n"); + NtWaitForSingleObject(NewSubsystem->Event, FALSE, NULL); + DPRINT1("Subsystem is ready!!!\n"); + } + + /* Subsystem is created, resumed, and initialized. Close handles and exit */ + NtClose(ProcessInformation.ThreadHandle); + Status = STATUS_SUCCESS; + DPRINT1("Returning success\n"); + goto Quickie2; + +Quickie: + /* This is the failure path. First check if we need to detach from session */ + if ((AttachedSessionId == -1) || (Flags & (SMP_POSIX_FLAG | SMP_OS2_FLAG))) + { + /* We were not attached, or did not launch subsystems that required it */ + DPRINT1("SMSS: Did not detach from Session Space: SessionId=%x Flags=%x Status=%x\n", + AttachedSessionId, + Flags | SMP_DEFERRED_FLAG, + Status); + } + else + { + /* Get the privilege we need for detachment */ + Status = SmpAcquirePrivilege(SE_LOAD_DRIVER_PRIVILEGE, &State); + if (!NT_SUCCESS(Status)) + { + /* We can't detach without it */ + DPRINT1("SMSS: Did not detach from Session Space: SessionId=%x Flags=%x Status=%x\n", + AttachedSessionId, + Flags | SMP_DEFERRED_FLAG, + Status); + } + else + { + /* Now detach from the session */ + Status = NtSetSystemInformation(SystemSessionDetach, + &AttachedSessionId, + sizeof(AttachedSessionId)); + if (!NT_SUCCESS(Status)) + { + /* Failed to detach. Note the DPRINT1 has a typo in Windows */ + DPRINT1("SMSS: SmpStartCsr, Couldn't Detach from Session Space. Status=%x\n", Status); + ASSERT(NT_SUCCESS(Status)); + } + else + { + /* Detachment worked, reset our attached session ID */ + AttachedSessionId = -1; + } + + /* And release the privilege we acquired */ + SmpReleasePrivilege(State); + } + } + + /* Since this is the failure path, terminate the subsystem process */ + NtTerminateProcess(ProcessInformation.ProcessHandle, Status); + NtClose(ProcessInformation.ThreadHandle); + +Quickie2: + /* This is the cleanup path -- first dereference our subsystems */ + RtlEnterCriticalSection(&SmpKnownSubSysLock); + if (Subsystem) SmpDereferenceSubsystem(Subsystem); + if (KnownSubsystem) SmpDereferenceSubsystem(KnownSubsystem); + + /* In the failure case, destroy the new subsystem we just created */ + if (!NT_SUCCESS(Status)) + { + RemoveEntryList(&NewSubsystem->Entry); + NtSetEvent(NewSubsystem->Event, 0); + if (NewSubsystem) SmpDereferenceSubsystem(NewSubsystem); + } + + /* Finally, we're all done! */ + RtlLeaveCriticalSection(&SmpKnownSubSysLock); + return Status; } NTSTATUS