Revert tree-restructure attempt: r66583, r66582, r66581, r66578, sauf ntdll changes...
[reactos.git] / reactos / subsystems / win32 / csrsrv / session.c
diff --git a/reactos/subsystems/win32/csrsrv/session.c b/reactos/subsystems/win32/csrsrv/session.c
new file mode 100644 (file)
index 0000000..20c7204
--- /dev/null
@@ -0,0 +1,578 @@
+/*
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS Client/Server Runtime SubSystem
+ * FILE:            subsystems/win32/csrsrv/session.c
+ * PURPOSE:         CSR Server DLL Session Implementation
+ * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "srv.h"
+
+#define NDEBUG
+#include <debug.h>
+
+/* DATA ***********************************************************************/
+
+RTL_CRITICAL_SECTION CsrNtSessionLock;
+LIST_ENTRY CsrNtSessionList;
+
+PSB_API_ROUTINE CsrServerSbApiDispatch[SbpMaxApiNumber - SbpCreateSession] =
+{
+    CsrSbCreateSession,
+    CsrSbTerminateSession,
+    CsrSbForeignSessionComplete,
+    CsrSbCreateProcess
+};
+
+PCHAR CsrServerSbApiName[SbpMaxApiNumber - SbpCreateSession] =
+{
+    "SbCreateSession",
+    "SbTerminateSession",
+    "SbForeignSessionComplete",
+    "SbCreateProcess"
+};
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+/*++
+ * @name CsrInitializeNtSessionList
+ *
+ * The CsrInitializeNtSessionList routine sets up support for CSR Sessions.
+ *
+ * @param None
+ *
+ * @return None
+ *
+ * @remarks None.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+CsrInitializeNtSessionList(VOID)
+{
+    /* Initialize the Session List */
+    InitializeListHead(&CsrNtSessionList);
+
+    /* Initialize the Session Lock */
+    return RtlInitializeCriticalSection(&CsrNtSessionLock);
+}
+
+/*++
+ * @name CsrAllocateNtSession
+ *
+ * The CsrAllocateNtSession routine allocates a new CSR NT Session.
+ *
+ * @param SessionId
+ *        Session ID of the CSR NT Session to allocate.
+ *
+ * @return Pointer to the newly allocated CSR NT Session.
+ *
+ * @remarks None.
+ *
+ *--*/
+PCSR_NT_SESSION
+NTAPI
+CsrAllocateNtSession(IN ULONG SessionId)
+{
+    PCSR_NT_SESSION NtSession;
+
+    /* Allocate an NT Session Object */
+    NtSession = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, sizeof(CSR_NT_SESSION));
+    if (NtSession)
+    {
+        /* Setup the Session Object */
+        NtSession->SessionId = SessionId;
+        NtSession->ReferenceCount = 1;
+
+        /* Insert it into the Session List */
+        CsrAcquireNtSessionLock();
+        InsertHeadList(&CsrNtSessionList, &NtSession->SessionLink);
+        CsrReleaseNtSessionLock();
+    }
+    else
+    {
+        ASSERT(NtSession != NULL);
+    }
+
+    /* Return the Session (or NULL) */
+    return NtSession;
+}
+
+/*++
+ * @name CsrReferenceNtSession
+ *
+ * The CsrReferenceNtSession increases the reference count of a CSR NT Session.
+ *
+ * @param Session
+ *        Pointer to the CSR NT Session to reference.
+ *
+ * @return None.
+ *
+ * @remarks None.
+ *
+ *--*/
+VOID
+NTAPI
+CsrReferenceNtSession(IN PCSR_NT_SESSION Session)
+{
+    /* Acquire the lock */
+    CsrAcquireNtSessionLock();
+
+    /* Sanity checks */
+    ASSERT(!IsListEmpty(&Session->SessionLink));
+    ASSERT(Session->SessionId != 0);
+    ASSERT(Session->ReferenceCount != 0);
+
+    /* Increase the reference count */
+    Session->ReferenceCount++;
+
+    /* Release the lock */
+    CsrReleaseNtSessionLock();
+}
+
+/*++
+ * @name CsrDereferenceNtSession
+ *
+ * The CsrDereferenceNtSession decreases the reference count of a
+ * CSR NT Session.
+ *
+ * @param Session
+ *        Pointer to the CSR NT Session to reference.
+ *
+ * @param ExitStatus
+ *        If this is the last reference to the session, this argument
+ *        specifies the exit status.
+ *
+ * @return None.
+ *
+ * @remarks CsrDereferenceNtSession will complete the session if
+ *          the last reference to it has been closed.
+ *
+ *--*/
+VOID
+NTAPI
+CsrDereferenceNtSession(IN PCSR_NT_SESSION Session,
+                        IN NTSTATUS ExitStatus)
+{
+    /* Acquire the lock */
+    CsrAcquireNtSessionLock();
+
+    /* Sanity checks */
+    ASSERT(!IsListEmpty(&Session->SessionLink));
+    ASSERT(Session->SessionId != 0);
+    ASSERT(Session->ReferenceCount != 0);
+
+    /* Dereference the Session Object */
+    if ((--Session->ReferenceCount) == 0)
+    {
+        /* Remove it from the list */
+        RemoveEntryList(&Session->SessionLink);
+
+        /* Release the lock */
+        CsrReleaseNtSessionLock();
+
+        /* Tell SM that we're done here */
+        SmSessionComplete(CsrSmApiPort, Session->SessionId, ExitStatus);
+
+        /* Free the Session Object */
+        RtlFreeHeap(CsrHeap, 0, Session);
+    }
+    else
+    {
+        /* Release the lock, the Session is still active */
+        CsrReleaseNtSessionLock();
+    }
+}
+
+/* SESSION MANAGER FUNCTIONS **************************************************/
+
+/*++
+ * @name CsrSbCreateSession
+ *
+ * The CsrSbCreateSession API is called by the Session Manager whenever a new
+ * session is created.
+ *
+ * @param ApiMessage
+ *        Pointer to the Session Manager API Message.
+ *
+ * @return TRUE in case of success, FALSE otherwise.
+ *
+ * @remarks The CsrSbCreateSession routine will initialize a new CSR NT
+ *          Session and allocate a new CSR Process for the subsystem process.
+ *
+ *--*/
+BOOLEAN
+NTAPI
+CsrSbCreateSession(IN PSB_API_MSG ApiMessage)
+{
+    PSB_CREATE_SESSION_MSG CreateSession = &ApiMessage->CreateSession;
+    HANDLE hProcess, hThread;
+    PCSR_PROCESS CsrProcess;
+    PCSR_THREAD CsrThread;
+    PCSR_SERVER_DLL ServerDll;
+    PVOID ProcessData;
+    NTSTATUS Status;
+    KERNEL_USER_TIMES KernelTimes;
+    ULONG i;
+
+    /* Save the Process and Thread Handles */
+    hProcess = CreateSession->ProcessInfo.ProcessHandle;
+    hThread = CreateSession->ProcessInfo.ThreadHandle;
+
+    /* Lock the Processes */
+    CsrAcquireProcessLock();
+
+    /* Allocate a new process */
+    CsrProcess = CsrAllocateProcess();
+    if (!CsrProcess)
+    {
+        /* Fail */
+        ApiMessage->ReturnValue = STATUS_NO_MEMORY;
+        CsrReleaseProcessLock();
+        return TRUE;
+    }
+
+    /* Set the exception port */
+    Status = NtSetInformationProcess(hProcess,
+                                     ProcessExceptionPort,
+                                     &CsrApiPort,
+                                     sizeof(HANDLE));
+
+    /* Check for success */
+    if (!NT_SUCCESS(Status))
+    {
+        /* Fail the request */
+        CsrDeallocateProcess(CsrProcess);
+        CsrReleaseProcessLock();
+
+        /* Strange as it seems, NTSTATUSes are actually returned */
+        return (BOOLEAN)STATUS_NO_MEMORY;
+    }
+
+    /* Get the Create Time */
+    Status = NtQueryInformationThread(hThread,
+                                      ThreadTimes,
+                                      &KernelTimes,
+                                      sizeof(KERNEL_USER_TIMES),
+                                      NULL);
+
+    /* Check for success */
+    if (!NT_SUCCESS(Status))
+    {
+        /* Fail the request */
+        CsrDeallocateProcess(CsrProcess);
+        CsrReleaseProcessLock();
+
+        /* Strange as it seems, NTSTATUSes are actually returned */
+        return (BOOLEAN)Status;
+    }
+
+    /* Allocate a new Thread */
+    CsrThread = CsrAllocateThread(CsrProcess);
+    if (!CsrThread)
+    {
+        /* Fail the request */
+        CsrDeallocateProcess(CsrProcess);
+        CsrReleaseProcessLock();
+
+        ApiMessage->ReturnValue = STATUS_NO_MEMORY;
+        return TRUE;
+    }
+
+    /* Setup the Thread Object */
+    CsrThread->CreateTime = KernelTimes.CreateTime;
+    CsrThread->ClientId = CreateSession->ProcessInfo.ClientId;
+    CsrThread->ThreadHandle = hThread;
+    ProtectHandle(hThread);
+    CsrThread->Flags = 0;
+
+    /* Insert it into the Process List */
+    Status = CsrInsertThread(CsrProcess, CsrThread);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Bail out */
+        CsrDeallocateProcess(CsrProcess);
+        CsrDeallocateThread(CsrThread);
+        CsrReleaseProcessLock();
+
+        /* Strange as it seems, NTSTATUSes are actually returned */
+        return (BOOLEAN)Status;
+    }
+
+    /* Setup Process Data */
+    CsrProcess->ClientId = CreateSession->ProcessInfo.ClientId;
+    CsrProcess->ProcessHandle = hProcess;
+    CsrProcess->NtSession = CsrAllocateNtSession(CreateSession->SessionId);
+
+    /* Set the Process Priority */
+    CsrSetBackgroundPriority(CsrProcess);
+
+    /* Get the first data location */
+    ProcessData = &CsrProcess->ServerData[CSR_SERVER_DLL_MAX];
+
+    /* Loop every DLL */
+    for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
+    {
+        /* Get the current Server */
+        ServerDll = CsrLoadedServerDll[i];
+
+        /* Check if the DLL is loaded and has Process Data */
+        if (ServerDll && ServerDll->SizeOfProcessData)
+        {
+            /* Write the pointer to the data */
+            CsrProcess->ServerData[i] = ProcessData;
+
+            /* Move to the next data location */
+            ProcessData = (PVOID)((ULONG_PTR)ProcessData +
+                                  ServerDll->SizeOfProcessData);
+        }
+        else
+        {
+            /* Nothing for this Process */
+            CsrProcess->ServerData[i] = NULL;
+        }
+    }
+
+    /* Insert the Process */
+    CsrInsertProcess(NULL, CsrProcess);
+
+    /* Activate the Thread */
+    ApiMessage->ReturnValue = NtResumeThread(hThread, NULL);
+
+    /* Release lock and return */
+    CsrReleaseProcessLock();
+    return TRUE;
+}
+
+/*++
+ * @name CsrSbForeignSessionComplete
+ *
+ * The CsrSbForeignSessionComplete API is called by the Session Manager
+ * whenever a foreign session is completed (ie: terminated).
+ *
+ * @param ApiMessage
+ *        Pointer to the Session Manager API Message.
+ *
+ * @return TRUE in case of success, FALSE otherwise.
+ *
+ * @remarks The CsrSbForeignSessionComplete API is not yet implemented.
+ *
+ *--*/
+BOOLEAN
+NTAPI
+CsrSbForeignSessionComplete(IN PSB_API_MSG ApiMessage)
+{
+    /* Deprecated/Unimplemented in NT */
+    ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED;
+    return TRUE;
+}
+
+/*++
+ * @name CsrSbTerminateSession
+ *
+ * The CsrSbTerminateSession API is called by the Session Manager
+ * whenever a foreign session should be destroyed.
+ *
+ * @param ApiMessage
+ *        Pointer to the Session Manager API Message.
+ *
+ * @return TRUE in case of success, FALSE otherwise.
+ *
+ * @remarks The CsrSbTerminateSession API is not yet implemented.
+ *
+ *--*/
+BOOLEAN
+NTAPI
+CsrSbTerminateSession(IN PSB_API_MSG ApiMessage)
+{
+    ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED;
+    return TRUE;
+}
+
+/*++
+ * @name CsrSbCreateProcess
+ *
+ * The CsrSbCreateProcess API is called by the Session Manager
+ * whenever a foreign session is created and a new process should be started.
+ *
+ * @param ApiMessage
+ *        Pointer to the Session Manager API Message.
+ *
+ * @return TRUE in case of success, FALSE otherwise.
+ *
+ * @remarks The CsrSbCreateProcess API is not yet implemented.
+ *
+ *--*/
+BOOLEAN
+NTAPI
+CsrSbCreateProcess(IN PSB_API_MSG ApiMessage)
+{
+    ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED;
+    return TRUE;
+}
+
+/*++
+ * @name CsrSbApiHandleConnectionRequest
+ *
+ * The CsrSbApiHandleConnectionRequest routine handles and accepts a new
+ * connection request to the SM API LPC Port.
+ *
+ * @param ApiMessage
+ *        Pointer to the incoming CSR API Message which contains the
+ *        connection request.
+ *
+ * @return STATUS_SUCCESS in case of success, or status code which caused
+ *         the routine to error.
+ *
+ * @remarks None.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+CsrSbApiHandleConnectionRequest(IN PSB_API_MSG Message)
+{
+    NTSTATUS Status;
+    REMOTE_PORT_VIEW RemotePortView;
+    HANDLE hPort;
+
+    /* Set the Port View Structure Length */
+    RemotePortView.Length = sizeof(REMOTE_PORT_VIEW);
+
+    /* Accept the connection */
+    Status = NtAcceptConnectPort(&hPort,
+                                 NULL,
+                                 &Message->h,
+                                 TRUE,
+                                 NULL,
+                                 &RemotePortView);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("CSRSS: Sb Accept Connection failed %lx\n", Status);
+        return Status;
+    }
+
+    /* Complete the Connection */
+    Status = NtCompleteConnectPort(hPort);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("CSRSS: Sb Complete Connection failed %lx\n",Status);
+    }
+
+    /* Return status */
+    return Status;
+}
+
+/*++
+ * @name CsrSbApiRequestThread
+ *
+ * The CsrSbApiRequestThread routine handles incoming messages or connection
+ * requests on the SM API LPC Port.
+ *
+ * @param Parameter
+ *        System-default user-defined parameter. Unused.
+ *
+ * @return The thread exit code, if the thread is terminated.
+ *
+ * @remarks Before listening on the port, the routine will first attempt
+ *          to connect to the user subsystem.
+ *
+ *--*/
+VOID
+NTAPI
+CsrSbApiRequestThread(IN PVOID Parameter)
+{
+    NTSTATUS Status;
+    SB_API_MSG ReceiveMsg;
+    PSB_API_MSG ReplyMsg = NULL;
+    PVOID PortContext;
+    ULONG MessageType;
+
+    /* Start the loop */
+    while (TRUE)
+    {
+        /* Wait for a message to come in */
+        Status = NtReplyWaitReceivePort(CsrSbApiPort,
+                                        &PortContext,
+                                        &ReplyMsg->h,
+                                        &ReceiveMsg.h);
+
+        /* Check if we didn't get success */
+        if (Status != STATUS_SUCCESS)
+        {
+            /* If we only got a warning, keep going */
+            if (NT_SUCCESS(Status)) continue;
+
+            /* We failed big time, so start out fresh */
+            ReplyMsg = NULL;
+            DPRINT1("CSRSS: ReceivePort failed - Status == %X\n", Status);
+            continue;
+        }
+
+        /* Save the message type */
+        MessageType = ReceiveMsg.h.u2.s2.Type;
+
+        /* Check if this is a connection request */
+        if (MessageType == LPC_CONNECTION_REQUEST)
+        {
+            /* Handle connection request */
+            CsrSbApiHandleConnectionRequest(&ReceiveMsg);
+
+            /* Start over */
+            ReplyMsg = NULL;
+            continue;
+        }
+
+        /* Check if the port died */
+        if (MessageType == LPC_PORT_CLOSED)
+        {
+            /* Close the handle if we have one */
+            if (PortContext) NtClose((HANDLE)PortContext);
+
+            /* Client died, start over */
+            ReplyMsg = NULL;
+            continue;
+        }
+        else if (MessageType == LPC_CLIENT_DIED)
+        {
+            /* Client died, start over */
+            ReplyMsg = NULL;
+            continue;
+        }
+
+        /*
+         * It's an API Message, check if it's within limits. If it's not,
+         * the NT Behaviour is to set this to the Maximum API.
+         */
+        if (ReceiveMsg.ApiNumber > SbpMaxApiNumber)
+        {
+            ReceiveMsg.ApiNumber = SbpMaxApiNumber;
+            DPRINT1("CSRSS: %lx is invalid Sb ApiNumber\n", ReceiveMsg.ApiNumber);
+        }
+
+        /* Reuse the message */
+        ReplyMsg = &ReceiveMsg;
+
+        /* Make sure that the message is supported */
+        if (ReceiveMsg.ApiNumber < SbpMaxApiNumber)
+        {
+            /* Call the API */
+            if (!CsrServerSbApiDispatch[ReceiveMsg.ApiNumber](&ReceiveMsg))
+            {
+                DPRINT1("CSRSS: %s Session Api called and failed\n",
+                        CsrServerSbApiName[ReceiveMsg.ApiNumber]);
+
+                /* It failed, so return nothing */
+                ReplyMsg = NULL;
+            }
+        }
+        else
+        {
+            /* We don't support this API Number */
+            ReplyMsg->ReturnValue = STATUS_NOT_IMPLEMENTED;
+        }
+    }
+}
+
+/* EOF */