Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / subsystems / win32 / csrsrv / procsup.c
diff --git a/subsystems/win32/csrsrv/procsup.c b/subsystems/win32/csrsrv/procsup.c
new file mode 100644 (file)
index 0000000..2d88a6b
--- /dev/null
@@ -0,0 +1,1408 @@
+/*
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS Client/Server Runtime SubSystem
+ * FILE:            subsystems/win32/csrsrv/procsup.c
+ * PURPOSE:         CSR Server DLL Process Management
+ * PROGRAMMERS:     ReactOS Portable Systems Group
+ *                  Alex Ionescu (alex@relsoft.net)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include <srv.h>
+
+#include <winuser.h>
+
+#define NDEBUG
+#include <debug.h>
+
+/* GLOBALS ********************************************************************/
+
+RTL_CRITICAL_SECTION CsrProcessLock;
+PCSR_PROCESS CsrRootProcess = NULL;
+SECURITY_QUALITY_OF_SERVICE CsrSecurityQos =
+{
+    sizeof(SECURITY_QUALITY_OF_SERVICE),
+    SecurityImpersonation,
+    SECURITY_STATIC_TRACKING,
+    FALSE
+};
+ULONG CsrProcessSequenceCount = 5;
+extern ULONG CsrTotalPerProcessDataLength;
+
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+/*++
+ * @name CsrSetToNormalPriority
+ *
+ * The CsrSetToNormalPriority routine sets the current NT Process'
+ * priority to the normal priority for CSR Processes.
+ *
+ * @param None.
+ *
+ * @return None.
+ *
+ * @remarks The "Normal" Priority corresponds to the Normal Foreground
+ *          Priority (9) plus a boost of 4.
+ *
+ *--*/
+VOID
+NTAPI
+CsrSetToNormalPriority(VOID)
+{
+    KPRIORITY BasePriority = (8 + 1) + 4;
+
+    /* Set the Priority */
+    NtSetInformationProcess(NtCurrentProcess(),
+                            ProcessBasePriority,
+                            &BasePriority,
+                            sizeof(BasePriority));
+}
+
+/*++
+ * @name CsrSetToShutdownPriority
+ *
+ * The CsrSetToShutdownPriority routine sets the current NT Process'
+ * priority to the boosted priority for CSR Processes doing shutdown.
+ * Additonally, it acquires the Shutdown Privilege required for shutdown.
+ *
+ * @param None.
+ *
+ * @return None.
+ *
+ * @remarks The "Shutdown" Priority corresponds to the Normal Foreground
+ *          Priority (9) plus a boost of 6.
+ *
+ *--*/
+VOID
+NTAPI
+CsrSetToShutdownPriority(VOID)
+{
+    KPRIORITY BasePriority = (8 + 1) + 6;
+    BOOLEAN Old;
+
+    /* Get the shutdown privilege */
+    if (NT_SUCCESS(RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
+                                      TRUE,
+                                      FALSE,
+                                      &Old)))
+    {
+        /* Set the Priority */
+        NtSetInformationProcess(NtCurrentProcess(),
+                                ProcessBasePriority,
+                                &BasePriority,
+                                sizeof(BasePriority));
+    }
+}
+
+/*++
+ * @name CsrProcessRefcountZero
+ *
+ * The CsrProcessRefcountZero routine is executed when a CSR Process has lost
+ * all its active references. It removes and de-allocates the CSR Process.
+ *
+ * @param CsrProcess
+ *        Pointer to the CSR Process that is to be deleted.
+ *
+ * @return None.
+ *
+ * @remarks Do not call this routine. It is reserved for the internal
+ *          thread management routines when a CSR Process has lost all
+ *          its references.
+ *
+ *          This routine is called with the Process Lock held.
+ *
+ *--*/
+VOID
+NTAPI
+CsrProcessRefcountZero(IN PCSR_PROCESS CsrProcess)
+{
+    ASSERT(ProcessStructureListLocked());
+
+    /* Remove the Process from the list */
+    CsrRemoveProcess(CsrProcess);
+
+    /* Check if there's a session */
+    if (CsrProcess->NtSession)
+    {
+        /* Dereference the Session */
+        CsrDereferenceNtSession(CsrProcess->NtSession, 0);
+    }
+
+    /* Close the Client Port if there is one */
+    if (CsrProcess->ClientPort) NtClose(CsrProcess->ClientPort);
+
+    /* Close the process handle */
+    NtClose(CsrProcess->ProcessHandle);
+
+    /* Free the Proces Object */
+    CsrDeallocateProcess(CsrProcess);
+}
+
+/*++
+ * @name CsrLockedDereferenceProcess
+ *
+ * The CsrLockedDereferenceProcess dereferences a CSR Process while the
+ * Process Lock is already being held.
+ *
+ * @param CsrProcess
+ *        Pointer to the CSR Process to be dereferenced.
+ *
+ * @return None.
+ *
+ * @remarks This routine will return with the Process Lock held.
+ *
+ *--*/
+VOID
+NTAPI
+CsrLockedDereferenceProcess(PCSR_PROCESS CsrProcess)
+{
+    LONG LockCount;
+
+    /* Decrease reference count */
+    LockCount = --CsrProcess->ReferenceCount;
+    ASSERT(LockCount >= 0);
+    if (LockCount == 0)
+    {
+        /* Call the generic cleanup code */
+        DPRINT1("Should kill process: %p\n", CsrProcess);
+        CsrAcquireProcessLock();
+        CsrProcessRefcountZero(CsrProcess);
+    }
+}
+
+/*++
+ * @name CsrAllocateProcess
+ * @implemented NT4
+ *
+ * The CsrAllocateProcess routine allocates a new CSR Process object.
+ *
+ * @return Pointer to the newly allocated CSR Process.
+ *
+ * @remarks None.
+ *
+ *--*/
+PCSR_PROCESS
+NTAPI
+CsrAllocateProcess(VOID)
+{
+    PCSR_PROCESS CsrProcess;
+    ULONG TotalSize;
+
+    /* Calculate the amount of memory this should take */
+    TotalSize = sizeof(CSR_PROCESS) +
+                (CSR_SERVER_DLL_MAX * sizeof(PVOID)) +
+                CsrTotalPerProcessDataLength;
+
+    /* Allocate a Process */
+    CsrProcess = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, TotalSize);
+    if (!CsrProcess) return NULL;
+
+    /* Handle the Sequence Number and protect against overflow */
+    CsrProcess->SequenceNumber = CsrProcessSequenceCount++;
+    if (CsrProcessSequenceCount < 5) CsrProcessSequenceCount = 5;
+
+    /* Increase the reference count */
+    CsrLockedReferenceProcess(CsrProcess);
+
+    /* Initialize the Thread List */
+    InitializeListHead(&CsrProcess->ThreadList);
+
+    /* Return the Process */
+    return CsrProcess;
+}
+
+/*++
+ * @name CsrLockedReferenceProcess
+ *
+ * The CsrLockedReferenceProcess references a CSR Process while the
+ * Process Lock is already being held.
+ *
+ * @param CsrProcess
+ *        Pointer to the CSR Process to be referenced.
+ *
+ * @return None.
+ *
+ * @remarks This routine will return with the Process Lock held.
+ *
+ *--*/
+VOID
+NTAPI
+CsrLockedReferenceProcess(IN PCSR_PROCESS CsrProcess)
+{
+    /* Increment the reference count */
+    ++CsrProcess->ReferenceCount;
+}
+
+/*++
+ * @name CsrInitializeProcessStructure
+ * @implemented NT4
+ *
+ * The CsrInitializeProcessStructure routine sets up support for CSR Processes
+ * and CSR Threads by initializing our own CSR Root Process.
+ *
+ * @param None.
+ *
+ * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
+ *
+ * @remarks None.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+CsrInitializeProcessStructure(VOID)
+{
+    NTSTATUS Status;
+    ULONG i;
+
+    /* Initialize the Lock */
+    Status = RtlInitializeCriticalSection(&CsrProcessLock);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Set up the Root Process */
+    CsrRootProcess = CsrAllocateProcess();
+    if (!CsrRootProcess) return STATUS_NO_MEMORY;
+
+    /* Set up the minimal information for it */
+    InitializeListHead(&CsrRootProcess->ListLink);
+    CsrRootProcess->ProcessHandle = (HANDLE)-1;
+    CsrRootProcess->ClientId = NtCurrentTeb()->ClientId;
+
+    /* Initialize the Thread Hash List */
+    for (i = 0; i < NUMBER_THREAD_HASH_BUCKETS; i++) InitializeListHead(&CsrThreadHashTable[i]);
+
+    /* Initialize the Wait Lock */
+    return RtlInitializeCriticalSection(&CsrWaitListsLock);
+}
+
+/*++
+ * @name CsrDeallocateProcess
+ *
+ * The CsrDeallocateProcess frees the memory associated with a CSR Process.
+ *
+ * @param CsrProcess
+ *        Pointer to the CSR Process to be freed.
+ *
+ * @return None.
+ *
+ * @remarks Do not call this routine. It is reserved for the internal
+ *          thread management routines when a CSR Process has been cleanly
+ *          dereferenced and killed.
+ *
+ *--*/
+VOID
+NTAPI
+CsrDeallocateProcess(IN PCSR_PROCESS CsrProcess)
+{
+    /* Free the process object from the heap */
+    RtlFreeHeap(CsrHeap, 0, CsrProcess);
+}
+
+/*++
+ * @name CsrRemoveProcess
+ *
+ * The CsrRemoveProcess function undoes a CsrInsertProcess operation and
+ * removes the CSR Process from the Process List and notifies Server DLLs
+ * of this removal.
+ *
+ * @param CsrProcess
+ *        Pointer to the CSR Process to remove.
+ *
+ * @return None.
+ *
+ * @remarks None.
+ *
+ *--*/
+VOID
+NTAPI
+CsrRemoveProcess(IN PCSR_PROCESS CsrProcess)
+{
+    PCSR_SERVER_DLL ServerDll;
+    ULONG i;
+    ASSERT(ProcessStructureListLocked());
+
+    /* Remove us from the Process List */
+    RemoveEntryList(&CsrProcess->ListLink);
+
+    /* Release the lock */
+    CsrReleaseProcessLock();
+
+    /* Loop every Server DLL */
+    for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
+    {
+        /* Get the Server DLL */
+        ServerDll = CsrLoadedServerDll[i];
+
+        /* Check if it's valid and if it has a Disconnect Callback */
+        if (ServerDll && ServerDll->DisconnectCallback)
+        {
+            /* Call it */
+            ServerDll->DisconnectCallback(CsrProcess);
+        }
+    }
+}
+
+/*++
+ * @name CsrInsertProcess
+ *
+ * The CsrInsertProcess routine inserts a CSR Process into the Process List
+ * and notifies Server DLLs of the creation of a new CSR Process.
+ *
+ * @param ParentProcess
+ *        Optional pointer to the Parent Process creating this CSR Process.
+ *
+ * @param CsrProcess
+ *        Pointer to the CSR Process which is to be inserted.
+ *
+ * @return None.
+ *
+ * @remarks None.
+ *
+ *--*/
+VOID
+NTAPI
+CsrInsertProcess(IN PCSR_PROCESS ParentProcess OPTIONAL,
+                 IN PCSR_PROCESS CsrProcess)
+{
+    PCSR_SERVER_DLL ServerDll;
+    ULONG i;
+    ASSERT(ProcessStructureListLocked());
+
+    /* Insert it into the Root List */
+    InsertTailList(&CsrRootProcess->ListLink, &CsrProcess->ListLink);
+
+    /* Notify the Server DLLs */
+    for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
+    {
+        /* Get the current Server DLL */
+        ServerDll = CsrLoadedServerDll[i];
+
+        /* Make sure it's valid and that it has callback */
+        if (ServerDll && ServerDll->NewProcessCallback)
+        {
+            ServerDll->NewProcessCallback(ParentProcess, CsrProcess);
+        }
+    }
+}
+
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+/*++
+ * @name CsrCreateProcess
+ * @implemented NT4
+ *
+ * The CsrCreateProcess routine creates a CSR Process object for an NT Process.
+ *
+ * @param hProcess
+ *        Handle to an existing NT Process to which to associate this
+ *        CSR Process.
+ *
+ * @param hThread
+ *        Handle to an existing NT Thread to which to create its
+ *        corresponding CSR Thread for this CSR Process.
+ *
+ * @param ClientId
+ *        Pointer to the Client ID structure of the NT Process to associate
+ *        with this CSR Process.
+ *
+ * @param NtSession
+ * @param Flags
+ * @param DebugCid
+ *
+ * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
+ *
+ * @remarks None.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+CsrCreateProcess(IN HANDLE hProcess,
+                 IN HANDLE hThread,
+                 IN PCLIENT_ID ClientId,
+                 IN PCSR_NT_SESSION NtSession,
+                 IN ULONG Flags,
+                 IN PCLIENT_ID DebugCid)
+{
+    PCSR_THREAD CurrentThread = CsrGetClientThread();
+    CLIENT_ID CurrentCid;
+    PCSR_PROCESS CurrentProcess;
+    PCSR_SERVER_DLL ServerDll;
+    PVOID ProcessData;
+    ULONG i;
+    PCSR_PROCESS CsrProcess;
+    NTSTATUS Status;
+    PCSR_THREAD CsrThread;
+    KERNEL_USER_TIMES KernelTimes;
+
+    /* Get the current CID and lock Processes */
+    CurrentCid = CurrentThread->ClientId;
+    CsrAcquireProcessLock();
+
+    /* Get the current CSR Thread */
+    CurrentThread = CsrLocateThreadByClientId(&CurrentProcess, &CurrentCid);
+    if (!CurrentThread)
+    {
+        /* We've failed to locate the thread */
+        CsrReleaseProcessLock();
+        return STATUS_THREAD_IS_TERMINATING;
+    }
+
+    /* Allocate a new Process Object */
+    CsrProcess = CsrAllocateProcess();
+    if (!CsrProcess)
+    {
+        /* Couldn't allocate Process */
+        CsrReleaseProcessLock();
+        return STATUS_NO_MEMORY;
+    }
+
+    /* Inherit the Process Data */
+    CurrentProcess = CurrentThread->Process;
+    ProcessData = &CsrProcess->ServerData[CSR_SERVER_DLL_MAX];
+    for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
+    {
+        /* Get the current Server */
+        ServerDll = CsrLoadedServerDll[i];
+
+        /* Check if the DLL is Loaded and has Per Process Data */
+        if (ServerDll && ServerDll->SizeOfProcessData)
+        {
+            /* Set the pointer */
+            CsrProcess->ServerData[i] = ProcessData;
+
+            /* Copy the Data */
+            RtlMoveMemory(ProcessData,
+                          CurrentProcess->ServerData[i],
+                          ServerDll->SizeOfProcessData);
+
+            /* Update next data pointer */
+            ProcessData = (PVOID)((ULONG_PTR)ProcessData +
+                                  ServerDll->SizeOfProcessData);
+        }
+        else
+        {
+            /* No data for this Server */
+            CsrProcess->ServerData[i] = NULL;
+        }
+    }
+
+    /* Set the Exception Port for us */
+    Status = NtSetInformationProcess(hProcess,
+                                     ProcessExceptionPort,
+                                     &CsrApiPort,
+                                     sizeof(CsrApiPort));
+    if (!NT_SUCCESS(Status))
+    {
+        /* Failed */
+        CsrDeallocateProcess(CsrProcess);
+        CsrReleaseProcessLock();
+        return STATUS_NO_MEMORY;
+    }
+
+    /* Check if CreateProcess got CREATE_NEW_PROCESS_GROUP */
+    if (Flags & CsrProcessCreateNewGroup)
+    {
+        /*
+         * We create the process group leader of a new process group, therefore
+         * its process group ID and sequence number are its own ones.
+         */
+        CsrProcess->ProcessGroupId = HandleToUlong(ClientId->UniqueProcess);
+        CsrProcess->ProcessGroupSequence = CsrProcess->SequenceNumber;
+    }
+    else
+    {
+        /* Inherit the process group ID and sequence number from the current process */
+        CsrProcess->ProcessGroupId = CurrentProcess->ProcessGroupId;
+        CsrProcess->ProcessGroupSequence = CurrentProcess->ProcessGroupSequence;
+    }
+
+    /* Check if this is a console process */
+    if (Flags & CsrProcessIsConsoleApp) CsrProcess->Flags |= CsrProcessIsConsoleApp;
+
+    /* Mask out non-debug flags */
+    Flags &= ~(CsrProcessIsConsoleApp | CsrProcessCreateNewGroup | CsrProcessPriorityFlags);
+
+    /* Check if every process will be debugged */
+    if (!(Flags) && (CurrentProcess->DebugFlags & CsrDebugProcessChildren))
+    {
+        /* Pass it on to the current process */
+        CsrProcess->DebugFlags = CsrDebugProcessChildren;
+        CsrProcess->DebugCid = CurrentProcess->DebugCid;
+    }
+
+    /* Check if Debugging was used on this process */
+    if ((Flags & (CsrDebugOnlyThisProcess | CsrDebugProcessChildren)) && (DebugCid))
+    {
+        /* Save the debug flag used */
+        CsrProcess->DebugFlags = Flags;
+
+        /* Save the CID */
+        CsrProcess->DebugCid = *DebugCid;
+    }
+
+    /* Check if Debugging is enabled */
+    if (CsrProcess->DebugFlags)
+    {
+        /* Set the Debug Port for us */
+        Status = NtSetInformationProcess(hProcess,
+                                         ProcessDebugPort,
+                                         &CsrApiPort,
+                                         sizeof(CsrApiPort));
+        ASSERT(NT_SUCCESS(Status));
+        if (!NT_SUCCESS(Status))
+        {
+            /* Failed */
+            CsrDeallocateProcess(CsrProcess);
+            CsrReleaseProcessLock();
+            return STATUS_NO_MEMORY;
+        }
+    }
+
+    /* Get the Thread Create Time */
+    Status = NtQueryInformationThread(hThread,
+                                      ThreadTimes,
+                                      &KernelTimes,
+                                      sizeof(KernelTimes),
+                                      NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Failed */
+        CsrDeallocateProcess(CsrProcess);
+        CsrReleaseProcessLock();
+        return STATUS_NO_MEMORY;
+    }
+
+    /* Allocate a CSR Thread Structure */
+    CsrThread = CsrAllocateThread(CsrProcess);
+    if (!CsrThread)
+    {
+        /* Failed */
+        CsrDeallocateProcess(CsrProcess);
+        CsrReleaseProcessLock();
+        return STATUS_NO_MEMORY;
+    }
+
+    /* Save the data we have */
+    CsrThread->CreateTime = KernelTimes.CreateTime;
+    CsrThread->ClientId = *ClientId;
+    CsrThread->ThreadHandle = hThread;
+    ProtectHandle(hThread);
+    CsrThread->Flags = 0;
+
+    /* Insert the Thread into the Process */
+    Status = CsrInsertThread(CsrProcess, CsrThread);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Bail out */
+        CsrDeallocateProcess(CsrProcess);
+        CsrDeallocateThread(CsrThread);
+        CsrReleaseProcessLock();
+        return Status;
+    }
+
+    /* Reference the session */
+    CsrReferenceNtSession(NtSession);
+    CsrProcess->NtSession = NtSession;
+
+    /* Setup Process Data */
+    CsrProcess->ClientId = *ClientId;
+    CsrProcess->ProcessHandle = hProcess;
+    CsrProcess->ShutdownLevel = 0x280;
+
+    /* Set the Priority to Background */
+    CsrSetBackgroundPriority(CsrProcess);
+
+    /* Insert the Process */
+    CsrInsertProcess(CurrentProcess, CsrProcess);
+
+    /* Release lock and return */
+    CsrReleaseProcessLock();
+    return Status;
+}
+
+/*++
+ * @name CsrDebugProcess
+ * @implemented NT4
+ *
+ * The CsrDebugProcess routine is deprecated in NT 5.1 and higher. It is
+ * exported only for compatibility with older CSR Server DLLs.
+ *
+ * @param CsrProcess
+ *        Deprecated.
+ *
+ * @return Deprecated
+ *
+ * @remarks Deprecated.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+CsrDebugProcess(IN PCSR_PROCESS CsrProcess)
+{
+    /* CSR does not handle debugging anymore */
+    DPRINT("CSRSRV: %s(0x%p) called\n", __FUNCTION__, CsrProcess);
+    return STATUS_UNSUCCESSFUL;
+}
+
+/*++
+ * @name CsrDebugProcessStop
+ * @implemented NT4
+ *
+ * The CsrDebugProcessStop routine is deprecated in NT 5.1 and higher. It is
+ * exported only for compatibility with older CSR Server DLLs.
+ *
+ * @param CsrProcess
+ *        Deprecated.
+ *
+ * @return Deprecated
+ *
+ * @remarks Deprecated.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+CsrDebugProcessStop(IN PCSR_PROCESS CsrProcess)
+{
+    /* CSR does not handle debugging anymore */
+    DPRINT("CSRSRV: %s(0x%p) called\n", __FUNCTION__, CsrProcess);
+    return STATUS_UNSUCCESSFUL;
+}
+
+/*++
+ * @name CsrDereferenceProcess
+ * @implemented NT4
+ *
+ * The CsrDereferenceProcess routine removes a reference from a CSR Process.
+ *
+ * @param CsrThread
+ *        Pointer to the CSR Process to dereference.
+ *
+ * @return None.
+ *
+ * @remarks If the reference count has reached zero (ie: the CSR Process has
+ *          no more active references), it will be deleted.
+ *
+ *--*/
+VOID
+NTAPI
+CsrDereferenceProcess(IN PCSR_PROCESS CsrProcess)
+{
+    LONG LockCount;
+
+    /* Acquire process lock */
+    CsrAcquireProcessLock();
+
+    /* Decrease reference count */
+    LockCount = --CsrProcess->ReferenceCount;
+    ASSERT(LockCount >= 0);
+    if (LockCount == 0)
+    {
+        /* Call the generic cleanup code */
+        CsrProcessRefcountZero(CsrProcess);
+    }
+    else
+    {
+        /* Just release the lock */
+        CsrReleaseProcessLock();
+    }
+}
+
+/*++
+ * @name CsrDestroyProcess
+ * @implemented NT4
+ *
+ * The CsrDestroyProcess routine destroys the CSR Process corresponding to
+ * a given Client ID.
+ *
+ * @param Cid
+ *        Pointer to the Client ID Structure corresponding to the CSR
+ *        Process which is about to be destroyed.
+ *
+ * @param ExitStatus
+ *        Unused.
+ *
+ * @return STATUS_SUCCESS in case of success, STATUS_THREAD_IS_TERMINATING
+ *         if the CSR Process is already terminating.
+ *
+ * @remarks None.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+CsrDestroyProcess(IN PCLIENT_ID Cid,
+                  IN NTSTATUS ExitStatus)
+{
+    PCSR_THREAD CsrThread;
+    PCSR_PROCESS CsrProcess;
+    CLIENT_ID ClientId = *Cid;
+    PLIST_ENTRY NextEntry;
+
+    /* Acquire lock */
+    CsrAcquireProcessLock();
+
+    /* Find the thread */
+    CsrThread = CsrLocateThreadByClientId(&CsrProcess, &ClientId);
+
+    /* Make sure we got one back, and that it's not already gone */
+    if (!(CsrThread) || (CsrProcess->Flags & CsrProcessTerminating))
+    {
+        /* Release the lock and return failure */
+        CsrReleaseProcessLock();
+        return STATUS_THREAD_IS_TERMINATING;
+    }
+
+    /* Set the terminated flag */
+    CsrProcess->Flags |= CsrProcessTerminating;
+
+    /* Get the List Pointers */
+    NextEntry = CsrProcess->ThreadList.Flink;
+    while (NextEntry != &CsrProcess->ThreadList)
+    {
+        /* Get the current thread entry */
+        CsrThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
+
+        /* Move to the next entry */
+        NextEntry = NextEntry->Flink;
+
+        /* Make sure the thread isn't already dead */
+        if (CsrThread->Flags & CsrThreadTerminated)
+        {
+            /* Go the the next thread */
+            continue;
+        }
+
+        /* Set the Terminated flag */
+        CsrThread->Flags |= CsrThreadTerminated;
+
+        /* Acquire the Wait Lock */
+        CsrAcquireWaitLock();
+
+        /* Do we have an active wait block? */
+        if (CsrThread->WaitBlock)
+        {
+            /* Notify waiters of termination */
+            CsrNotifyWaitBlock(CsrThread->WaitBlock,
+                               NULL,
+                               NULL,
+                               NULL,
+                               CsrProcessTerminating,
+                               TRUE);
+        }
+
+        /* Release the Wait Lock */
+        CsrReleaseWaitLock();
+
+        /* Dereference the thread */
+        CsrLockedDereferenceThread(CsrThread);
+    }
+
+    /* Release the Process Lock and return success */
+    CsrReleaseProcessLock();
+    return STATUS_SUCCESS;
+}
+
+/*++
+ * @name CsrGetProcessLuid
+ * @implemented NT4
+ *
+ * The CsrGetProcessLuid routine gets the LUID of the given process.
+ *
+ * @param hProcess
+ *        Optional handle to the process whose LUID should be returned.
+ *
+ * @param Luid
+ *        Pointer to a LUID Pointer which will receive the CSR Process' LUID.
+ *
+ * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
+ *
+ * @remarks If hProcess is not supplied, then the current thread's token will
+ *          be used. If that too is missing, then the current process' token
+ *          will be used.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+CsrGetProcessLuid(IN HANDLE hProcess OPTIONAL,
+                  OUT PLUID Luid)
+{
+    HANDLE hToken = NULL;
+    NTSTATUS Status;
+    ULONG Length;
+    PTOKEN_STATISTICS TokenStats;
+
+    /* Check if we have a handle to a CSR Process */
+    if (!hProcess)
+    {
+        /* We don't, so try opening the Thread's Token */
+        Status = NtOpenThreadToken(NtCurrentThread(),
+                                   TOKEN_QUERY,
+                                   FALSE,
+                                   &hToken);
+
+        /* Check for success */
+        if (!NT_SUCCESS(Status))
+        {
+            /* If we got some other failure, then return and quit */
+            if (Status != STATUS_NO_TOKEN) return Status;
+
+            /* We don't have a Thread Token, use a Process Token */
+            hProcess = NtCurrentProcess();
+            hToken = NULL;
+        }
+    }
+
+    /* Check if we have a token by now */
+    if (!hToken)
+    {
+        /* No token yet, so open the Process Token */
+        Status = NtOpenProcessToken(hProcess,
+                                    TOKEN_QUERY,
+                                    &hToken);
+        if (!NT_SUCCESS(Status))
+        {
+            /* Still no token, return the error */
+            return Status;
+        }
+    }
+
+    /* Now get the size we'll need for the Token Information */
+    Status = NtQueryInformationToken(hToken,
+                                     TokenStatistics,
+                                     NULL,
+                                     0,
+                                     &Length);
+
+    /* Allocate memory for the Token Info */
+    if (!(TokenStats = RtlAllocateHeap(CsrHeap, 0, Length)))
+    {
+        /* Fail and close the token */
+        NtClose(hToken);
+        return STATUS_NO_MEMORY;
+    }
+
+    /* Now query the information */
+    Status = NtQueryInformationToken(hToken,
+                                     TokenStatistics,
+                                     TokenStats,
+                                     Length,
+                                     &Length);
+
+    /* Close the handle */
+    NtClose(hToken);
+
+    /* Check for success */
+    if (NT_SUCCESS(Status))
+    {
+        /* Return the LUID */
+        *Luid = TokenStats->AuthenticationId;
+    }
+
+    /* Free the query information */
+    RtlFreeHeap(CsrHeap, 0, TokenStats);
+
+    /* Return the Status */
+    return Status;
+}
+
+/*++
+ * @name CsrImpersonateClient
+ * @implemented NT4
+ *
+ * The CsrImpersonateClient will impersonate the given CSR Thread.
+ *
+ * @param CsrThread
+ *        Pointer to the CSR Thread to impersonate.
+ *
+ * @return TRUE if impersonation succeeded, FALSE otherwise.
+ *
+ * @remarks Impersonation can be recursive.
+ *
+ *--*/
+BOOLEAN
+NTAPI
+CsrImpersonateClient(IN PCSR_THREAD CsrThread)
+{
+    NTSTATUS Status;
+    PCSR_THREAD CurrentThread = CsrGetClientThread();
+
+    /* Use the current thread if none given */
+    if (!CsrThread) CsrThread = CurrentThread;
+
+    /* Still no thread, something is wrong */
+    if (!CsrThread)
+    {
+        /* Failure */
+        return FALSE;
+    }
+
+    /* Make the call */
+    Status = NtImpersonateThread(NtCurrentThread(),
+                                 CsrThread->ThreadHandle,
+                                 &CsrSecurityQos);
+
+    if (!NT_SUCCESS(Status))
+    {
+        /* Failure */
+        DPRINT1("CSRSS: Can't impersonate client thread - Status = %lx\n", Status);
+        // if (Status != STATUS_BAD_IMPERSONATION_LEVEL) DbgBreakPoint();
+        return FALSE;
+    }
+
+    /* Increase the impersonation count for the current thread */
+    if (CurrentThread) ++CurrentThread->ImpersonationCount;
+
+    /* Return Success */
+    return TRUE;
+}
+
+/*++
+ * @name CsrLockProcessByClientId
+ * @implemented NT4
+ *
+ * The CsrLockProcessByClientId routine locks the CSR Process corresponding
+ * to the given Process ID and optionally returns it.
+ *
+ * @param Pid
+ *        Process ID corresponding to the CSR Process which will be locked.
+ *
+ * @param CsrProcess
+ *        Optional pointer to a CSR Process pointer which will hold the
+ *        CSR Process corresponding to the given Process ID.
+ *
+ * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
+ *
+ * @remarks Locking a CSR Process is defined as acquiring an extra
+ *          reference to it and returning with the Process Lock held.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+CsrLockProcessByClientId(IN HANDLE Pid,
+                         OUT PCSR_PROCESS *CsrProcess)
+{
+    PLIST_ENTRY NextEntry;
+    PCSR_PROCESS CurrentProcess = NULL;
+    NTSTATUS Status = STATUS_UNSUCCESSFUL;
+
+    /* Acquire the lock */
+    CsrAcquireProcessLock();
+
+    /* Assume failure */
+    ASSERT(CsrProcess != NULL);
+    *CsrProcess = NULL;
+
+    /* Setup the List Pointers */
+    NextEntry = &CsrRootProcess->ListLink;
+    do
+    {
+        /* Get the Process */
+        CurrentProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink);
+
+        /* Check for PID Match */
+        if (CurrentProcess->ClientId.UniqueProcess == Pid)
+        {
+            Status = STATUS_SUCCESS;
+            break;
+        }
+
+        /* Move to the next entry */
+        NextEntry = NextEntry->Flink;
+    } while (NextEntry != &CsrRootProcess->ListLink);
+
+    /* Check if we didn't find it in the list */
+    if (!NT_SUCCESS(Status))
+    {
+        /* Nothing found, release the lock */
+        CsrReleaseProcessLock();
+    }
+    else
+    {
+        /* Lock the found process and return it */
+        CsrLockedReferenceProcess(CurrentProcess);
+        *CsrProcess = CurrentProcess;
+    }
+
+    /* Return the result */
+    return Status;
+}
+
+/*++
+ * @name CsrRevertToSelf
+ * @implemented NT4
+ *
+ * The CsrRevertToSelf routine will attempt to remove an active impersonation.
+ *
+ * @param None.
+ *
+ * @return TRUE if the reversion was succesful, FALSE otherwise.
+ *
+ * @remarks Impersonation can be recursive; as such, the impersonation token
+ *          will only be deleted once the CSR Thread's impersonaton count
+ *          has reached zero.
+ *
+ *--*/
+BOOLEAN
+NTAPI
+CsrRevertToSelf(VOID)
+{
+    NTSTATUS Status;
+    PCSR_THREAD CurrentThread = CsrGetClientThread();
+    HANDLE ImpersonationToken = NULL;
+
+    /* Check if we have a Current Thread */
+    if (CurrentThread)
+    {
+        /* Make sure impersonation is on */
+        if (!CurrentThread->ImpersonationCount)
+        {
+            DPRINT1("CSRSS: CsrRevertToSelf called while not impersonating\n");
+            // DbgBreakPoint();
+            return FALSE;
+        }
+        else if ((--CurrentThread->ImpersonationCount) > 0)
+        {
+            /* Success; impersonation count decreased but still not zero */
+            return TRUE;
+        }
+    }
+
+    /* Impersonation has been totally removed, revert to ourselves */
+    Status = NtSetInformationThread(NtCurrentThread(),
+                                    ThreadImpersonationToken,
+                                    &ImpersonationToken,
+                                    sizeof(ImpersonationToken));
+
+    /* Return TRUE or FALSE */
+    return NT_SUCCESS(Status);
+}
+
+/*++
+ * @name CsrSetBackgroundPriority
+ * @implemented NT4
+ *
+ * The CsrSetBackgroundPriority routine sets the priority for the given CSR
+ * Process as a Background priority.
+ *
+ * @param CsrProcess
+ *        Pointer to the CSR Process whose priority will be modified.
+ *
+ * @return None.
+ *
+ * @remarks None.
+ *
+ *--*/
+VOID
+NTAPI
+CsrSetBackgroundPriority(IN PCSR_PROCESS CsrProcess)
+{
+    PROCESS_FOREGROUND_BACKGROUND ProcessPriority;
+
+    /* Set the Foreground bit off */
+    ProcessPriority.Foreground = FALSE;
+
+    /* Set the new priority */
+    NtSetInformationProcess(CsrProcess->ProcessHandle,
+                            ProcessForegroundInformation,
+                            &ProcessPriority,
+                            sizeof(ProcessPriority));
+}
+
+/*++
+ * @name CsrSetForegroundPriority
+ * @implemented NT4
+ *
+ * The CsrSetForegroundPriority routine sets the priority for the given CSR
+ * Process as a Foreground priority.
+ *
+ * @param CsrProcess
+ *        Pointer to the CSR Process whose priority will be modified.
+ *
+ * @return None.
+ *
+ * @remarks None.
+ *
+ *--*/
+VOID
+NTAPI
+CsrSetForegroundPriority(IN PCSR_PROCESS CsrProcess)
+{
+    PROCESS_FOREGROUND_BACKGROUND ProcessPriority;
+
+    /* Set the Foreground bit on */
+    ProcessPriority.Foreground = TRUE;
+
+    /* Set the new priority */
+    NtSetInformationProcess(CsrProcess->ProcessHandle,
+                            ProcessForegroundInformation,
+                            &ProcessPriority,
+                            sizeof(ProcessPriority));
+}
+
+/*++
+ * @name FindProcessForShutdown
+ *
+ * The FindProcessForShutdown routine returns a CSR Process which is ready
+ * to be shutdown, and sets the appropriate shutdown flags for it.
+ *
+ * @param CallerLuid
+ *        Pointer to the LUID of the CSR Process calling this routine.
+ *
+ * @return Pointer to a CSR Process which is ready to be shutdown.
+ *
+ * @remarks None.
+ *
+ *--*/
+PCSR_PROCESS
+NTAPI
+FindProcessForShutdown(IN PLUID CallerLuid)
+{
+    PCSR_PROCESS CsrProcess, ReturnCsrProcess = NULL;
+    PCSR_THREAD CsrThread;
+    NTSTATUS Status;
+    ULONG Level = 0;
+    LUID ProcessLuid;
+    LUID SystemLuid = SYSTEM_LUID;
+    PLIST_ENTRY NextEntry;
+
+    /* Set the List Pointers */
+    NextEntry = CsrRootProcess->ListLink.Flink;
+    while (NextEntry != &CsrRootProcess->ListLink)
+    {
+        /* Get the process */
+        CsrProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink);
+
+        /* Move to the next entry */
+        NextEntry = NextEntry->Flink;
+
+        /* Skip this process if it's already been processed */
+        if (CsrProcess->Flags & CsrProcessSkipShutdown) continue;
+
+        /* Get the LUID of this process */
+        Status = CsrGetProcessLuid(CsrProcess->ProcessHandle, &ProcessLuid);
+
+        /* Check if we didn't get access to the LUID */
+        if (Status == STATUS_ACCESS_DENIED)
+        {
+            /* Check if we have any threads */
+            if (CsrProcess->ThreadCount)
+            {
+                /* Impersonate one of the threads and retry */
+                CsrThread = CONTAINING_RECORD(CsrProcess->ThreadList.Flink,
+                                              CSR_THREAD,
+                                              Link);
+                if (CsrImpersonateClient(CsrThread))
+                {
+                    Status = CsrGetProcessLuid(NULL, &ProcessLuid);
+                    CsrRevertToSelf();
+                }
+                else
+                {
+                    Status = STATUS_BAD_IMPERSONATION_LEVEL;
+                }
+            }
+        }
+
+        if (!NT_SUCCESS(Status))
+        {
+            /* We didn't have access, so skip it */
+            CsrProcess->Flags |= CsrProcessSkipShutdown;
+            continue;
+        }
+
+        /* Check if this is the System LUID */
+        if (RtlEqualLuid(&ProcessLuid, &SystemLuid))
+        {
+            /* Mark this process */
+            CsrProcess->ShutdownFlags |= CsrShutdownSystem;
+        }
+        else if (!RtlEqualLuid(&ProcessLuid, CallerLuid))
+        {
+            /* Our LUID doesn't match with the caller's */
+            CsrProcess->ShutdownFlags |= CsrShutdownOther;
+        }
+
+        /* Check if we're past the previous level */
+        if ((CsrProcess->ShutdownLevel > Level) || !ReturnCsrProcess)
+        {
+            /* Update the level */
+            Level = CsrProcess->ShutdownLevel;
+
+            /* Set the final process */
+            ReturnCsrProcess = CsrProcess;
+        }
+    }
+
+    /* Check if we found a process */
+    if (ReturnCsrProcess)
+    {
+        /* Skip this one next time */
+        ReturnCsrProcess->Flags |= CsrProcessSkipShutdown;
+    }
+
+    return ReturnCsrProcess;
+}
+
+/*++
+ * @name CsrShutdownProcesses
+ * @implemented NT4
+ *
+ * The CsrShutdownProcesses routine shuts down every CSR Process possible
+ * and calls each Server DLL's shutdown notification.
+ *
+ * @param CallerLuid
+ *        Pointer to the LUID of the CSR Process that is ordering the
+ *        shutdown.
+ *
+ * @param Flags
+ *        Flags to send to the shutdown notification routine.
+ *
+ * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
+ *
+ * @remarks None.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+CsrShutdownProcesses(IN PLUID CallerLuid,
+                     IN ULONG Flags)
+{
+    PLIST_ENTRY NextEntry;
+    PCSR_PROCESS CsrProcess;
+    NTSTATUS Status;
+    BOOLEAN FirstTry;
+    ULONG i;
+    PCSR_SERVER_DLL ServerDll;
+    ULONG Result = 0;
+
+    /* Acquire process lock */
+    CsrAcquireProcessLock();
+
+    /* Add shutdown flag */
+    CsrRootProcess->ShutdownFlags |= CsrShutdownSystem;
+
+    /* Get the list pointers */
+    NextEntry = CsrRootProcess->ListLink.Flink;
+    while (NextEntry != &CsrRootProcess->ListLink)
+    {
+        /* Get the Process */
+        CsrProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink);
+
+        /* Move to the next entry */
+        NextEntry = NextEntry->Flink;
+
+        /* Remove the skip flag, set shutdown flags to 0 */
+        CsrProcess->Flags &= ~CsrProcessSkipShutdown;
+        CsrProcess->ShutdownFlags = 0;
+    }
+
+    /* Set shutdown Priority */
+    CsrSetToShutdownPriority();
+
+    /* Start looping */
+    while (TRUE)
+    {
+        /* Find the next process to shutdown */
+        CsrProcess = FindProcessForShutdown(CallerLuid);
+        if (!CsrProcess) break;
+
+        /* Increase reference to process */
+        CsrLockedReferenceProcess(CsrProcess);
+
+        FirstTry = TRUE;
+        while (TRUE)
+        {
+            /* Loop all the servers */
+            for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
+            {
+                /* Get the current server */
+                ServerDll = CsrLoadedServerDll[i];
+
+                /* Check if it's valid and if it has a Shutdown Process Callback */
+                if (ServerDll && ServerDll->ShutdownProcessCallback)
+                {
+                    /* Release the lock, make the callback, and acquire it back */
+                    CsrReleaseProcessLock();
+                    Result = ServerDll->ShutdownProcessCallback(CsrProcess,
+                                                                Flags,
+                                                                FirstTry);
+                    CsrAcquireProcessLock();
+
+                    /* Check the result */
+                    if (Result == CsrShutdownCsrProcess)
+                    {
+                        /* The callback unlocked the process */
+                        break;
+                    }
+                    else if (Result == CsrShutdownCancelled)
+                    {
+                        /* Check if this was a forced shutdown */
+                        if (Flags & EWX_FORCE)
+                        {
+                            DPRINT1("Process %x cancelled forced shutdown (Dll = %d)\n",
+                                     CsrProcess->ClientId.UniqueProcess, i);
+                            DbgBreakPoint();
+                        }
+
+                        /* Shutdown was cancelled, unlock and exit */
+                        CsrReleaseProcessLock();
+                        Status = STATUS_CANCELLED;
+                        goto Quickie;
+                    }
+                }
+            }
+
+            /* No matches during the first try, so loop again */
+            if (FirstTry && (Result == CsrShutdownNonCsrProcess))
+            {
+                FirstTry = FALSE;
+                continue;
+            }
+
+            /* Second try, break out */
+            break;
+        }
+
+        /* We've reached the final loop here, so dereference */
+        if (i == CSR_SERVER_DLL_MAX) CsrLockedDereferenceProcess(CsrProcess);
+    }
+
+    /* Success path */
+    CsrReleaseProcessLock();
+    Status = STATUS_SUCCESS;
+
+Quickie:
+    /* Return to normal priority */
+    CsrSetToNormalPriority();
+
+    return Status;
+}
+
+/*++
+ * @name CsrUnlockProcess
+ * @implemented NT4
+ *
+ * The CsrUnlockProcess undoes a previous CsrLockProcessByClientId operation.
+ *
+ * @param CsrProcess
+ *        Pointer to a previously locked CSR Process.
+ *
+ * @return STATUS_SUCCESS.
+ *
+ * @remarks This routine must be called with the Process Lock held.
+ *
+ *--*/
+NTSTATUS
+NTAPI
+CsrUnlockProcess(IN PCSR_PROCESS CsrProcess)
+{
+    /* Dereference the process */
+    CsrLockedDereferenceProcess(CsrProcess);
+
+    /* Release the lock and return */
+    CsrReleaseProcessLock();
+    return STATUS_SUCCESS;
+}
+
+/* EOF */