[BASESRV][KERNEL32][NTVDM]
[reactos.git] / subsystems / win / basesrv / vdm.c
index 674f91c..8d3af6f 100644 (file)
@@ -203,7 +203,7 @@ NTSTATUS NTAPI BaseSrvCreatePairWaitHandles(PHANDLE ServerEvent, PHANDLE ClientE
     return Status;
 }
 
-VOID BaseSrvFreeVDMInfo(PVDM_COMMAND_INFO CommandInfo)
+VOID NTAPI BaseSrvFreeVDMInfo(PVDM_COMMAND_INFO CommandInfo)
 {
     /* Free the allocated structure members */
     if (CommandInfo->CmdLine != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->CmdLine);
@@ -219,6 +219,61 @@ VOID BaseSrvFreeVDMInfo(PVDM_COMMAND_INFO CommandInfo)
     RtlFreeHeap(BaseSrvHeap, 0, CommandInfo);
 }
 
+VOID NTAPI BaseSrvCleanupVdmRecords(ULONG ProcessId)
+{
+    PLIST_ENTRY i;
+    PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
+    PVDM_DOS_RECORD DosRecord;
+
+    /* Enter the critical section */
+    RtlEnterCriticalSection(&DosCriticalSection);
+
+    /* Search for a record that has the same process handle */
+    for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
+    {
+        ConsoleRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
+
+        if (ConsoleRecord->ProcessId == ProcessId)
+        {
+            /* Cleanup the DOS records */
+            while (ConsoleRecord->DosListHead.Flink != &ConsoleRecord->DosListHead)
+            {
+                DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
+                                              VDM_DOS_RECORD,
+                                              Entry);
+
+                /* Set the event and close it */
+                NtSetEvent(DosRecord->ServerEvent, NULL);
+                NtClose(DosRecord->ServerEvent);
+
+                /* Remove the DOS entry */
+                if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
+                RemoveEntryList(&DosRecord->Entry);
+                RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
+            }
+
+            if (ConsoleRecord->CurrentDirs != NULL)
+            {
+                /* Free the current directories */
+                RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord->CurrentDirs);
+                ConsoleRecord->CurrentDirs = NULL;
+                ConsoleRecord->CurDirsLength = 0;
+            }
+
+            /* Close the event handle */
+            if (ConsoleRecord->ServerEvent) NtClose(ConsoleRecord->ServerEvent);
+
+            /* Remove the console record */
+            i = i->Blink;
+            RemoveEntryList(&ConsoleRecord->Entry);
+            RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
+        }
+    }
+
+    /* Leave the critical section */
+    RtlLeaveCriticalSection(&DosCriticalSection);
+}
+
 BOOLEAN NTAPI BaseSrvCopyCommand(PBASE_CHECK_VDM CheckVdmRequest, PVDM_DOS_RECORD DosRecord)
 {
     BOOLEAN Success = FALSE;
@@ -367,7 +422,7 @@ Cleanup:
 }
 
 NTSTATUS NTAPI BaseSrvFillCommandInfo(PVDM_COMMAND_INFO CommandInfo,
-                                  PBASE_GET_NEXT_VDM_COMMAND Message)
+                                      PBASE_GET_NEXT_VDM_COMMAND Message)
 {
     /* Copy the data */
     Message->iTask = CommandInfo->TaskId;
@@ -850,19 +905,58 @@ CSR_API(BaseSrvGetNextVDMCommand)
         GetNextVdmCommandRequest->iTask = ConsoleRecord->SessionId;
         GetNextVdmCommandRequest->WaitObjectForVDM = NULL;
 
-        // HACK: I'm not sure if this should happen...
-        for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
+        if (GetNextVdmCommandRequest->VDMState & VDM_GET_FIRST_COMMAND)
         {
-            DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
-            if (DosRecord->State == VDM_NOT_READY)
+            /* Check if the DOS record list is empty */
+            if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead)
             {
-                /* If NTVDM is asking for a new command, it means these are done */
-                DosRecord->State = VDM_READY; 
+                Status = STATUS_INVALID_PARAMETER;
+                goto Cleanup;
+            }
 
-                NtSetEvent(DosRecord->ServerEvent, NULL);
-                NtClose(DosRecord->ServerEvent);
-                DosRecord->ServerEvent = NULL;
+            /* Get the first DOS record */
+            DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink, VDM_DOS_RECORD, Entry);
+
+            /* Make sure its command information is still there */
+            if (DosRecord->CommandInfo == NULL)
+            {
+                Status = STATUS_INVALID_PARAMETER;
+                goto Cleanup;
             }
+
+            /* Fill the command information */
+            Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
+            goto Cleanup;
+        }
+
+        /* Check if we should set the state of a running DOS record to ready */
+        if (!(GetNextVdmCommandRequest->VDMState
+            & (VDM_FLAG_FIRST_TASK | VDM_FLAG_RETRY | VDM_FLAG_NESTED_TASK)))
+        {
+            /* Search for a DOS record that is currently running */
+            for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
+            {
+                DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
+                if (DosRecord->State == VDM_NOT_READY) break;
+            }
+
+            /* Check if we found any */
+            if (i == &ConsoleRecord->DosListHead)
+            {
+                Status = STATUS_INVALID_PARAMETER;
+                goto Cleanup;
+            }
+
+            /* Set the exit code */
+            DosRecord->ExitCode = GetNextVdmCommandRequest->ExitCode;
+
+            /* Update the VDM state */
+            DosRecord->State = VDM_READY; 
+
+            /* Notify all waiting threads that the task is finished */
+            NtSetEvent(DosRecord->ServerEvent, NULL);
+            NtClose(DosRecord->ServerEvent);
+            DosRecord->ServerEvent = NULL;
         }
 
         /* Search for a DOS record that isn't loaded yet */
@@ -874,16 +968,40 @@ CSR_API(BaseSrvGetNextVDMCommand)
 
         if (i != &ConsoleRecord->DosListHead)
         {
-            /* Fill the command information */
-            Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
-            if (!NT_SUCCESS(Status)) goto Cleanup;
+            /* DOS tasks which haven't been loaded yet should have a command info structure */
+            ASSERT(DosRecord->CommandInfo != NULL);
+
+            /* Check if the caller only wants environment data */
+            if (GetNextVdmCommandRequest->VDMState & VDM_GET_ENVIRONMENT)
+            {
+                if (GetNextVdmCommandRequest->EnvLen < DosRecord->CommandInfo->EnvLen)
+                {
+                    /* Not enough space was reserved */
+                    GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen;
+                    Status = STATUS_BUFFER_OVERFLOW;
+                    goto Cleanup;
+                }
 
-            /* Free the command information, it's no longer needed */
-            BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
-            DosRecord->CommandInfo = NULL;
+                /* Copy the environment data */
+                RtlMoveMemory(GetNextVdmCommandRequest->Env,
+                              DosRecord->CommandInfo->Env,
+                              DosRecord->CommandInfo->EnvLen);
 
-            /* Update the VDM state */
-            DosRecord->State = VDM_NOT_READY;
+                /* Return the actual size to the caller */
+                GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen;
+            }
+            else
+            {
+                /* Fill the command information */
+                Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
+
+                /* Free the command information, it's no longer needed */
+                BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
+                DosRecord->CommandInfo = NULL;
+
+                /* Update the VDM state */
+                GetNextVdmCommandRequest->VDMState = DosRecord->State = VDM_NOT_READY;
+            }
 
             Status = STATUS_SUCCESS;
             goto Cleanup;
@@ -897,21 +1015,25 @@ CSR_API(BaseSrvGetNextVDMCommand)
     }
 
     /* There is no command yet */
-    if (ConsoleRecord->ServerEvent)
-    {
-        /* Reset the event */
-        NtResetEvent(ConsoleRecord->ServerEvent, NULL);
-    }
-    else
+    if ((GetNextVdmCommandRequest->VDMState & (VDM_FLAG_DONT_WAIT | VDM_FLAG_RETRY))
+        != (VDM_FLAG_DONT_WAIT | VDM_FLAG_RETRY))
     {
-        /* Create a pair of wait handles */
-        Status = BaseSrvCreatePairWaitHandles(&ConsoleRecord->ServerEvent,
-                                              &ConsoleRecord->ClientEvent);
-        if (!NT_SUCCESS(Status)) goto Cleanup;
-    }
+        if (ConsoleRecord->ServerEvent)
+        {
+            /* Reset the event */
+            NtResetEvent(ConsoleRecord->ServerEvent, NULL);
+        }
+        else
+        {
+            /* Create a pair of wait handles */
+            Status = BaseSrvCreatePairWaitHandles(&ConsoleRecord->ServerEvent,
+                                                  &ConsoleRecord->ClientEvent);
+            if (!NT_SUCCESS(Status)) goto Cleanup;
+        }
 
-    /* Return the client event handle */
-    GetNextVdmCommandRequest->WaitObjectForVDM = ConsoleRecord->ClientEvent;
+        /* Return the client event handle */
+        GetNextVdmCommandRequest->WaitObjectForVDM = ConsoleRecord->ClientEvent;
+    }
 
 Cleanup:
     /* Leave the critical section */