Index: trunk/src/VBox/Additions/common/VBoxService/Makefile.kmk
===================================================================
--- trunk/src/VBox/Additions/common/VBoxService/Makefile.kmk	(revision 39278)
+++ trunk/src/VBox/Additions/common/VBoxService/Makefile.kmk	(revision 39279)
@@ -69,7 +69,5 @@
  VBoxService_SOURCES    += \
  	VBoxServiceControl.cpp \
- 	VBoxServiceControlExec.cpp \
-	VBoxServiceControlExecThread.cpp \
-	VBoxServicePipeBuf.cpp
+	VBoxServiceControlExecThread.cpp
 endif
 
Index: trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp
===================================================================
--- trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp	(revision 39278)
+++ trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp	(revision 39279)
@@ -39,16 +39,18 @@
 /** The control interval (milliseconds). */
 uint32_t g_ControlInterval = 0;
-/** The semaphore we're blocking on. */
-static RTSEMEVENTMULTI      g_hControlEvent = NIL_RTSEMEVENTMULTI;
+/** The semaphore we're blocking our main control thread on. */
+RTSEMEVENTMULTI             g_hControlEvent = NIL_RTSEMEVENTMULTI;
 /** The guest control service client ID. */
 static uint32_t             g_GuestControlSvcClientID = 0;
 /** How many started guest processes are kept into memory for supplying
- *  information to the host. Default is 5 processes. If 0 is specified,
+ *  information to the host. Default is 25 processes. If 0 is specified,
  *  the maximum number of processes is unlimited. */
-uint32_t                    g_GuestControlProcsMaxKept = 5;
+uint32_t                    g_GuestControlProcsMaxKept = 25;
 /** List of guest control threads. */
 RTLISTNODE                  g_GuestControlThreads;
 /** Critical section protecting g_GuestControlExecThreads. */
 RTCRITSECT                  g_GuestControlThreadsCritSect;
+
+static int VBoxServiceControlStartAllowed(bool *pbAllowed);
 
 /** @copydoc VBOXSERVICE::pfnPreInit */
@@ -191,14 +193,14 @@
 
                 case HOST_EXEC_CMD:
-                    rc = VBoxServiceControlExecHandleCmdStartProcess(g_GuestControlSvcClientID, uNumParms);
+                    rc = VBoxServiceControlHandleCmdStartProc(g_GuestControlSvcClientID, uNumParms);
                     break;
 
                 case HOST_EXEC_SET_INPUT:
                     /** @todo Make buffer size configurable via guest properties/argv! */
-                    rc = VBoxServiceControlExecHandleCmdSetInput(g_GuestControlSvcClientID, uNumParms, _1M /* Buffer size */);
+                    rc = VBoxServiceControlHandleCmdSetInput(g_GuestControlSvcClientID, uNumParms, _1M /* Buffer size */);
                     break;
 
                 case HOST_EXEC_GET_OUTPUT:
-                    rc = VBoxServiceControlExecHandleCmdGetOutput(g_GuestControlSvcClientID, uNumParms);
+                    rc = VBoxServiceControlHandleCmdGetOutput(g_GuestControlSvcClientID, uNumParms);
                     break;
 
@@ -231,4 +233,347 @@
 
 
+/**
+ * Handles starting processes on the guest.
+ *
+ * @returns IPRT status code.
+ * @param   u32ClientId                 The HGCM client session ID.
+ * @param   uNumParms                   The number of parameters the host is offering.
+ */
+int VBoxServiceControlHandleCmdStartProc(uint32_t uClientID, uint32_t uNumParms)
+{
+    uint32_t uContextID;
+    char szCmd[_1K];
+    uint32_t uFlags;
+    char szArgs[_1K];
+    uint32_t uNumArgs;
+    char szEnv[_64K];
+    uint32_t cbEnv = sizeof(szEnv);
+    uint32_t uNumEnvVars;
+    char szUser[128];
+    char szPassword[128];
+    uint32_t uTimeLimitMS;
+
+#if 0 /* for valgrind */
+    RT_ZERO(szCmd);
+    RT_ZERO(szArgs);
+    RT_ZERO(szEnv);
+    RT_ZERO(szUser);
+    RT_ZERO(szPassword);
+#endif
+
+    if (uNumParms != 11)
+        return VERR_INVALID_PARAMETER;
+
+    int rc = VbglR3GuestCtrlExecGetHostCmd(uClientID,
+                                           uNumParms,
+                                           &uContextID,
+                                           /* Command */
+                                           szCmd,      sizeof(szCmd),
+                                           /* Flags */
+                                           &uFlags,
+                                           /* Arguments */
+                                           szArgs,     sizeof(szArgs), &uNumArgs,
+                                           /* Environment */
+                                           szEnv, &cbEnv, &uNumEnvVars,
+                                           /* Credentials */
+                                           szUser,     sizeof(szUser),
+                                           szPassword, sizeof(szPassword),
+                                           /* Timelimit */
+                                           &uTimeLimitMS);
+    if (RT_SUCCESS(rc))
+    {
+#ifdef DEBUG
+        VBoxServiceVerbose(3, "ControlExec: Start process szCmd=%s, uFlags=%u, szArgs=%s, szEnv=%s, szUser=%s, szPW=%s, uTimeout=%u\n",
+                           szCmd, uFlags, uNumArgs ? szArgs : "<None>", uNumEnvVars ? szEnv : "<None>", szUser, szPassword, uTimeLimitMS);
+#endif
+        bool fAllowed = false;
+        int rc = VBoxServiceControlStartAllowed(&fAllowed);
+        if (RT_FAILURE(rc))
+            VBoxServiceError("ControlExec: Error determining whether process can be started or not, rc=%Rrc\n", rc);
+
+        if (   RT_SUCCESS(rc)
+            && fAllowed)
+        {
+            int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
+            if (RT_SUCCESS(rc))
+            {
+                /** @todo Put the following params into a struct! */
+                RTLISTNODE *pThreadNode;
+                rc = VBoxServiceControlThreadStart(uClientID, uContextID,
+                                                   szCmd, uFlags, szArgs, uNumArgs,
+                                                   szEnv, cbEnv, uNumEnvVars,
+                                                   szUser, szPassword, uTimeLimitMS,
+                                                   &pThreadNode);
+                if (RT_SUCCESS(rc))
+                {
+                    /* Insert thread node into thread list. */
+                    /*rc =*/ RTListAppend(&g_GuestControlThreads, pThreadNode);
+                }
+
+                int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
+                if (RT_SUCCESS(rc))
+                    rc = rc2;
+            }
+        }
+        else /* Process start is not allowed due to policy settings. */
+        {
+            /* Tell the host. */
+            rc = VbglR3GuestCtrlExecReportStatus(uClientID, uContextID, 0 /* PID, invalid. */,
+                                                 PROC_STS_ERROR, VERR_MAX_PROCS_REACHED,
+                                                 NULL /* pvData */, 0 /* cbData */);
+        }
+    }
+    else
+        VBoxServiceError("ControlExec: Failed to retrieve exec start command! Error: %Rrc\n", rc);
+    return rc;
+}
+
+
+/**
+ * Gets output from stdout/stderr of a specified guest process.
+ *
+ * @return  IPRT status code.
+ * @param   uPID                    PID of process to retrieve the output from.
+ * @param   uHandleId               Stream ID (stdout = 0, stderr = 2) to get the output from.
+ * @param   uTimeout                Timeout (in ms) to wait for output becoming available.
+ * @param   pvBuf                   Pointer to a pre-allocated buffer to store the output.
+ * @param   cbBuf                   Size (in bytes) of the pre-allocated buffer.
+ * @param   pcbRead                 Pointer to number of bytes read.  Optional.
+ */
+int VBoxServiceControlExecGetOutput(uint32_t uPID, uint32_t uHandleId, uint32_t uTimeout,
+                                    void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
+{
+    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
+    /* pcbRead is optional. */
+
+    int rc = VINF_SUCCESS;
+
+    VBOXSERVICECTRLREQUEST ctrlRequest;
+    ctrlRequest.cbData = cbBuf;
+    ctrlRequest.pvData = (uint8_t*)pvBuf;
+
+    switch (uHandleId)
+    {
+        case OUTPUT_HANDLE_ID_STDERR:
+            ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_STDERR_READ;
+            break;
+
+        case OUTPUT_HANDLE_ID_STDOUT:
+        case OUTPUT_HANDLE_ID_STDOUT_DEPRECATED:
+            ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_STDOUT_READ;
+            break;
+
+        default:
+            rc = VERR_INVALID_PARAMETER;
+            break;
+    }
+
+    if (RT_SUCCESS(rc))
+        rc = VBoxServiceControlExecThreadPerform(uPID, &ctrlRequest);
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcbRead)
+            *pcbRead = ctrlRequest.cbData;
+    }
+
+    return rc;
+}
+
+
+/**
+ * Injects input to a specified running process.
+ *
+ * @return  IPRT status code.
+ * @param   uPID                    PID of process to set the input for.
+ * @param   fPendingClose           Flag indicating whether this is the last input block sent to the process.
+ * @param   pvBuf                   Pointer to a buffer containing the actual input data.
+ * @param   cbBuf                   Size (in bytes) of the input buffer data.
+ * @param   pcbWritten              Pointer to number of bytes written to the process.  Optional.
+ */
+int VBoxServiceControlSetInput(uint32_t uPID, bool fPendingClose,
+                               void *pvBuf, uint32_t cbBuf,
+                               uint32_t *pcbWritten)
+{
+    AssertPtrReturn(pvBuf, VERR_INVALID_PARAMETER);
+    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
+    /* pcbWritten is optional. */
+
+    int rc = VINF_SUCCESS;
+
+    VBOXSERVICECTRLREQUEST ctrlRequest;
+    ctrlRequest.cbData  = cbBuf;
+    ctrlRequest.pvData  = pvBuf;
+    ctrlRequest.enmType = fPendingClose
+                        ? VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF : VBOXSERVICECTRLREQUEST_STDIN_WRITE;
+    if (RT_SUCCESS(rc))
+        rc = VBoxServiceControlExecThreadPerform(uPID, &ctrlRequest);
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcbWritten)
+            *pcbWritten = ctrlRequest.cbData;
+    }
+
+    return rc;
+}
+
+
+/**
+ * Handles input for a started process by copying the received data into its
+ * stdin pipe.
+ *
+ * @returns IPRT status code.
+ * @param   u32ClientId                 The HGCM client session ID.
+ * @param   uNumParms                   The number of parameters the host is offering.
+ * @param   cMaxBufSize                 The maximum buffer size for retrieving the input data.
+ */
+int VBoxServiceControlHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize)
+{
+    uint32_t uContextID;
+    uint32_t uPID;
+    uint32_t uFlags;
+    uint32_t cbSize;
+
+    AssertReturn(RT_IS_POWER_OF_TWO(cbMaxBufSize), VERR_INVALID_PARAMETER);
+    uint8_t *pabBuffer = (uint8_t*)RTMemAlloc(cbMaxBufSize);
+    AssertPtrReturn(pabBuffer, VERR_NO_MEMORY);
+
+    uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status sent back to the host. */
+    uint32_t cbWritten = 0; /* Number of bytes written to the guest. */
+
+    /*
+     * Ask the host for the input data.
+     */
+    int rc = VbglR3GuestCtrlExecGetHostCmdInput(u32ClientId, uNumParms,
+                                                &uContextID, &uPID, &uFlags,
+                                                pabBuffer, cbMaxBufSize, &cbSize);
+    if (RT_FAILURE(rc))
+    {
+        VBoxServiceError("ControlExec: [PID %u]: Failed to retrieve exec input command! Error: %Rrc\n",
+                         uPID, rc);
+    }
+    else if (cbSize >  cbMaxBufSize)
+    {
+        VBoxServiceError("ControlExec: [PID %u]: Too much input received! cbSize=%u, cbMaxBufSize=%u\n",
+                         uPID, cbSize, cbMaxBufSize);
+        rc = VERR_INVALID_PARAMETER;
+    }
+    else
+    {
+        /*
+         * Is this the last input block we need to deliver? Then let the pipe know ...
+         */
+        bool fPendingClose = false;
+        if (uFlags & INPUT_FLAG_EOF)
+        {
+            fPendingClose = true;
+            VBoxServiceVerbose(4, "ControlExec: [PID %u]: Got last input block of size %u ...\n",
+                               uPID, cbSize);
+        }
+
+        rc = VBoxServiceControlSetInput(uPID, fPendingClose, pabBuffer,
+                                                  cbSize, &cbWritten);
+        VBoxServiceVerbose(4, "ControlExec: [PID %u]: Written input, rc=%Rrc, uFlags=0x%x, fPendingClose=%d, cbSize=%u, cbWritten=%u\n",
+                           uPID, rc, uFlags, fPendingClose, cbSize, cbWritten);
+        if (RT_SUCCESS(rc))
+        {
+            if (cbWritten || !cbSize) /* Did we write something or was there anything to write at all? */
+            {
+                uStatus = INPUT_STS_WRITTEN;
+                uFlags = 0;
+            }
+        }
+        else
+        {
+            if (rc == VERR_BAD_PIPE)
+                uStatus = INPUT_STS_TERMINATED;
+            else if (rc == VERR_BUFFER_OVERFLOW)
+                uStatus = INPUT_STS_OVERFLOW;
+        }
+    }
+    RTMemFree(pabBuffer);
+
+    /*
+     * If there was an error and we did not set the host status
+     * yet, then do it now.
+     */
+    if (   RT_FAILURE(rc)
+        && uStatus == INPUT_STS_UNDEFINED)
+    {
+        uStatus = INPUT_STS_ERROR;
+        uFlags = rc;
+    }
+    Assert(uStatus > INPUT_STS_UNDEFINED);
+
+    VBoxServiceVerbose(3, "ControlExec: [PID %u]: Input processed, CID=%u, uStatus=%u, uFlags=0x%x, cbWritten=%u\n",
+                       uPID, uContextID, uStatus, uFlags, cbWritten);
+
+    /* Note: Since the context ID is unique the request *has* to be completed here,
+     *       regardless whether we got data or not! Otherwise the progress object
+     *       on the host never will get completed! */
+    rc = VbglR3GuestCtrlExecReportStatusIn(u32ClientId, uContextID, uPID,
+                                           uStatus, uFlags, (uint32_t)cbWritten);
+
+    if (RT_FAILURE(rc))
+        VBoxServiceError("ControlExec: [PID %u]: Failed to report input status! Error: %Rrc\n",
+                         uPID, rc);
+    return rc;
+}
+
+
+/**
+ * Handles the guest control output command.
+ *
+ * @return  IPRT status code.
+ * @param   u32ClientId     idClient    The HGCM client session ID.
+ * @param   uNumParms       cParms      The number of parameters the host is
+ *                                      offering.
+ */
+int VBoxServiceControlHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms)
+{
+    uint32_t uContextID;
+    uint32_t uPID;
+    uint32_t uHandleID;
+    uint32_t uFlags;
+
+    int rc = VbglR3GuestCtrlExecGetHostCmdOutput(u32ClientId, uNumParms,
+                                                 &uContextID, &uPID, &uHandleID, &uFlags);
+    if (RT_SUCCESS(rc))
+    {
+        uint32_t cbRead = 0;
+        uint8_t *pBuf = (uint8_t*)RTMemAlloc(_64K);
+        if (pBuf)
+        {
+            rc = VBoxServiceControlExecGetOutput(uPID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */,
+                                                 pBuf, _64K /* cbSize */, &cbRead);
+            if (RT_SUCCESS(rc))
+                VBoxServiceVerbose(3, "ControlExec: [PID %u]: Got output, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u\n",
+                                   uPID, uContextID, cbRead, uHandleID, uFlags);
+            else
+                VBoxServiceError("ControlExec: [PID %u]: Failed to retrieve output, CID=%u, uHandle=%u, rc=%Rrc\n",
+                                 uPID, uContextID, uHandleID, rc);
+            /* Note: Since the context ID is unique the request *has* to be completed here,
+             *       regardless whether we got data or not! Otherwise the progress object
+             *       on the host never will get completed! */
+            /* cbRead now contains actual size. */
+            int rc2 = VbglR3GuestCtrlExecSendOut(u32ClientId, uContextID, uPID, uHandleID, uFlags,
+                                                 pBuf, cbRead);
+            if (RT_SUCCESS(rc))
+                rc = rc2;
+            RTMemFree(pBuf);
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+
+    if (RT_FAILURE(rc))
+        VBoxServiceError("ControlExec: [PID %u]: Error handling output command! Error: %Rrc\n",
+                         uPID, rc);
+    return rc;
+}
+
+
 /** @copydoc VBOXSERVICE::pfnStop */
 static DECLCALLBACK(void) VBoxServiceControlStop(void)
@@ -236,5 +581,5 @@
     VBoxServiceVerbose(3, "Control: Stopping ...\n");
 
-    /** @todo Later, figure what to do if we're in RTProcWait(). it's a very
+    /** @todo Later, figure what to do if we're in RTProcWait(). It's a very
      *        annoying call since doesn't support timeouts in the posix world. */
     RTSemEventMultiSignal(g_hControlEvent);
@@ -253,25 +598,4 @@
 
 
-void VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread)
-{
-    AssertPtrReturnVoid(pThread);
-    ASMAtomicXchgBool(&pThread->fShutdown, true);
-}
-
-
-int VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread)
-{
-    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
-    int rc = VINF_SUCCESS;
-    if (   pThread->Thread != NIL_RTTHREAD
-        && !pThread->fShutdown) /* Only shutdown threads which aren't yet. */
-    {
-        /* Wait a bit ... */
-        rc = RTThreadWait(pThread->Thread, 30 * 1000 /* Wait 30 seconds max. */, NULL);
-    }
-    return rc;
-}
-
-
 static void VBoxServiceControlDestroyThreads(void)
 {
@@ -282,41 +606,30 @@
     {
         /* Signal all threads that we want to shutdown. */
-        PVBOXSERVICECTRLTHREAD pNode;
-        RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
-            VBoxServiceControlThreadSignalShutdown(pNode);
+        PVBOXSERVICECTRLTHREAD pThread;
+        RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
+            VBoxServiceControlThreadSignalShutdown(pThread);
 
         /* Wait for threads to shutdown. */
-        RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
-        {
-            int rc2 = VBoxServiceControlThreadWaitForShutdown(pNode);
+        RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
+        {
+            int rc2 = VBoxServiceControlExecThreadShutdown(pThread);
             if (RT_FAILURE(rc2))
-                VBoxServiceError("Control: Thread failed to stop; rc2=%Rrc\n", rc2);
-
-            /* Destroy thread specific data. */
-            switch (pNode->enmType)
-            {
-                case kVBoxServiceCtrlThreadDataExec:
-                    VBoxServiceControlExecThreadDataDestroy((PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData);
-                    break;
-
-                default:
-                    break;
-            }
+                VBoxServiceError("Control: Guest process thread failed to stop; rc2=%Rrc\n", rc2);
         }
 
         /* Finally destroy thread list. */
-        pNode = RTListGetFirst(&g_GuestControlThreads, VBOXSERVICECTRLTHREAD, Node);
-        while (pNode)
-        {
-            PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pNode->Node, VBOXSERVICECTRLTHREAD, Node);
-            bool fLast = RTListNodeIsLast(&g_GuestControlThreads, &pNode->Node);
-
-            RTListNodeRemove(&pNode->Node);
-            RTMemFree(pNode);
+        pThread = RTListGetFirst(&g_GuestControlThreads, VBOXSERVICECTRLTHREAD, Node);
+        while (pThread)
+        {
+            PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLTHREAD, Node);
+            bool fLast = RTListNodeIsLast(&g_GuestControlThreads, &pThread->Node);
+
+            VBoxServiceControlRemoveThread(pThread);
+            VBoxServiceControlExecThreadDestroy(pThread);
 
             if (fLast)
                 break;
 
-            pNode = pNext;
+            pThread = pNext;
         }
 
@@ -325,4 +638,5 @@
             rc = rc2;
     }
+
     RTCritSectDelete(&g_GuestControlThreadsCritSect);
 }
@@ -343,4 +657,104 @@
         RTSemEventMultiDestroy(g_hControlEvent);
         g_hControlEvent = NIL_RTSEMEVENTMULTI;
+    }
+}
+
+
+static int VBoxServiceControlStartAllowed(bool *pbAllowed)
+{
+    AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER);
+
+    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Check if we're respecting our memory policy by checking
+         * how many guest processes are started and served already.
+         */
+        bool fLimitReached = false;
+        if (g_GuestControlProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
+        {
+            uint32_t uProcsRunning = 0;
+            uint32_t uProcsStopped = 0;
+            PVBOXSERVICECTRLTHREAD pThread;
+            RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
+            {
+    // THREAD LOCKING!!
+                Assert(pThread->fStarted != pThread->fStopped);
+                if (pThread->fStarted)
+                    uProcsRunning++;
+                else if (pThread->fStopped)
+                    uProcsStopped++;
+                else
+                    AssertMsgFailed(("ControlExec: Guest process neither started nor stopped!?\n"));
+            }
+
+            VBoxServiceVerbose(2, "ControlExec: Maximum served guest processes set to %u, running=%u, stopped=%u\n",
+                               g_GuestControlProcsMaxKept, uProcsRunning, uProcsStopped);
+
+            int32_t iProcsLeft = (g_GuestControlProcsMaxKept - uProcsRunning - 1);
+            if (iProcsLeft < 0)
+            {
+                VBoxServiceVerbose(3, "ControlExec: Maximum running guest processes reached (%u)\n",
+                                   g_GuestControlProcsMaxKept);
+                fLimitReached = true;
+            }
+        }
+
+        *pbAllowed = !fLimitReached;
+
+        int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
+        if (RT_SUCCESS(rc))
+            rc = rc2;
+    }
+
+    return rc;
+}
+
+
+/**
+ * Finds a (formerly) started process given by its PID.
+ *
+ * @return  PVBOXSERVICECTRLTHREAD      Process structure if found, otherwise NULL.
+ * @param   uPID                        PID to search for.
+ */
+const PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadByPID(uint32_t uPID)
+{
+    PVBOXSERVICECTRLTHREAD pNode = NULL;
+    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
+    if (RT_SUCCESS(rc))
+    {
+        PVBOXSERVICECTRLTHREAD pNodeCur;
+        RTListForEach(&g_GuestControlThreads, pNodeCur, VBOXSERVICECTRLTHREAD, Node)
+        {
+            if (pNodeCur->uPID == uPID)
+            {
+                pNode = pNodeCur;
+                break;
+            }
+        }
+
+        int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
+        if (RT_SUCCESS(rc))
+            rc = rc2;
+    }
+
+    return pNode;
+}
+
+
+void VBoxServiceControlRemoveThread(PVBOXSERVICECTRLTHREAD pThread)
+{
+    if (!pThread)
+        return;
+
+    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
+    if (RT_SUCCESS(rc))
+    {
+        RTListNodeRemove(&pThread->Node);
+
+        int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
+        if (RT_SUCCESS(rc))
+            rc = rc2;
     }
 }
@@ -357,5 +771,6 @@
     "Host-driven Guest Control",
     /* pszUsage. */
-    "              [--control-interval <ms>] [--control-procs-max-kept <x>]"
+    "              [--control-interval <ms>] [--control-procs-max-kept <x>]\n"
+    "              [--control-procs-mem-std[in|out|err] <KB>]"
     ,
     /* pszOptions. */
@@ -364,5 +779,5 @@
     "    --control-procs-max-kept\n"
     "                            Specifies how many started guest processes are\n"
-    "                            kept into memory to work with.\n"
+    "                            kept into memory to work with. Default is 25.\n"
     ,
     /* methods */
Index: trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExec.cpp
===================================================================
--- trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExec.cpp	(revision 39278)
+++ 	(revision )
@@ -1,1444 +1,0 @@
-/* $Id$ */
-/** @file
- * VBoxServiceControlExec - Utility functions for process execution.
- */
-
-/*
- * Copyright (C) 2011 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-
-/*******************************************************************************
-*   Header Files                                                               *
-*******************************************************************************/
-#include <iprt/assert.h>
-#include <iprt/crc.h>
-#include <iprt/ctype.h>
-#include <iprt/env.h>
-#include <iprt/file.h>
-#include <iprt/getopt.h>
-#include <iprt/handle.h>
-#include <iprt/mem.h>
-#include <iprt/path.h>
-#include <iprt/param.h>
-#include <iprt/pipe.h>
-#include <iprt/poll.h>
-#include <iprt/process.h>
-#include <iprt/string.h>
-#include <iprt/stream.h>
-#include <iprt/thread.h>
-#include <VBox/version.h>
-#include <VBox/VBoxGuestLib.h>
-#include <VBox/HostServices/GuestControlSvc.h>
-
-#include "VBoxServiceInternal.h"
-#include "VBoxServiceUtils.h"
-#include "VBoxServicePipeBuf.h"
-#include "VBoxServiceControlExecThread.h"
-
-using namespace guestControl;
-
-extern RTLISTNODE g_GuestControlThreads;
-extern RTCRITSECT g_GuestControlThreadsCritSect;
-
-
-/**
- * Handle an error event on standard input.
- *
- * @returns IPRT status code.
- * @param   hPollSet            The polling set.
- * @param   fPollEvt            The event mask returned by RTPollNoResume.
- * @param   phStdInW            The standard input pipe handle.
- * @param   pStdInBuf           The standard input buffer.
- */
-static int VBoxServiceControlExecProcHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
-                                                           PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf)
-{
-    int rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
-    /* Don't assert if writable handle is not in poll set anymore. */
-    if (   RT_FAILURE(rc)
-        && rc != VERR_POLL_HANDLE_ID_NOT_FOUND)
-    {
-        AssertRC(rc);
-    }
-
-    /* Close writable stdin pipe. */
-    rc = RTPipeClose(*phStdInW);
-    AssertRC(rc);
-    *phStdInW = NIL_RTPIPE;
-
-    /* Mark the stdin buffer as dead; we're not using it anymore. */
-    rc = VBoxServicePipeBufSetStatus(pStdInBuf, false /* Disabled */);
-    AssertRC(rc);
-
-    /* Remove stdin error handle from set. */
-    rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_ERROR);
-    /* Don't assert if writable handle is not in poll set anymore. */
-    if (   RT_FAILURE(rc)
-        && rc != VERR_POLL_HANDLE_ID_NOT_FOUND)
-    {
-        AssertRC(rc);
-    }
-    else
-        rc = VINF_SUCCESS;
-
-    return rc;
-}
-
-
-/**
- * Try write some more data to the standard input of the child.
- *
- * @returns IPRT status code.
- * @retval  VINF_TRY_AGAIN if there is still data left in the buffer.
- *
- * @param   hPollSet            The polling set.
- * @param   pStdInBuf           The standard input buffer.
- * @param   hStdInW             The standard input pipe.
- * @param   pfClose             Pointer to a flag whether the pipe needs to be closed afterwards.
- */
-static int VBoxServiceControlExecProcWriteStdIn(RTPOLLSET hPollSet, PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf, RTPIPE hStdInW,
-                                                size_t *pcbWritten, bool *pfClose)
-{
-    AssertPtrReturn(pStdInBuf, VERR_INVALID_PARAMETER);
-    AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
-    AssertPtrReturn(pfClose, VERR_INVALID_PARAMETER);
-
-    size_t cbLeft;
-    int rc = VBoxServicePipeBufWriteToPipe(pStdInBuf, hStdInW, pcbWritten, &cbLeft);
-
-    /* If we have written all data which is in the buffer set the close flag. */
-    *pfClose = (cbLeft == 0) && VBoxServicePipeBufIsClosing(pStdInBuf);
-
-    if (   !*pcbWritten
-        && VBoxServicePipeBufIsEnabled(pStdInBuf))
-    {
-        /*
-         * Nothing else left to write now? Remove the writable event from the poll set
-         * to not trigger too high CPU loads.
-         */
-        int rc2 = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
-        AssertRC(rc2);
-    }
-
-    VBoxServiceVerbose(3, "VBoxServiceControlExecProcWriteStdIn: Written=%u, Left=%u, rc=%Rrc\n",
-                       *pcbWritten, cbLeft, rc);
-    return rc;
-}
-
-
-/**
- * Handle an event indicating we can write to the standard input pipe of the
- * child process.
- *
- * @returns IPRT status code.
- * @param   hPollSet            The polling set.
- * @param   fPollEvt            The event mask returned by RTPollNoResume.
- * @param   phStdInW            The standard input pipe.
- * @param   pStdInBuf           The standard input buffer.
- * @param   pcbWritten          Where to return the number of bytes written.
- */
-static int  VBoxServiceControlExecProcHandleStdInWritableEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
-                                                               PVBOXSERVICECTRLEXECPIPEBUF pStdInBuf, size_t *pcbWritten)
-{
-    AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
-    int rc;
-    if (!(fPollEvt & RTPOLL_EVT_ERROR))
-    {
-        bool fClose;
-        rc = VBoxServiceControlExecProcWriteStdIn(hPollSet,
-                                                  pStdInBuf, *phStdInW,
-                                                  pcbWritten, &fClose);
-        if (   rc == VINF_TRY_AGAIN
-            || rc == VERR_MORE_DATA)
-            rc = VINF_SUCCESS;
-        if (RT_FAILURE(rc))
-        {
-            if (   rc == VERR_BAD_PIPE
-                || rc == VERR_BROKEN_PIPE)
-            {
-                rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
-                AssertRC(rc);
-            }
-            else
-            {
-                /** @todo Do we need to do something about this error condition? */
-                AssertRC(rc);
-            }
-        }
-        else if (fClose)
-        {
-            /* If the pipe needs to be closed, do so. */
-            rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
-        }
-    }
-    else
-    {
-        *pcbWritten = 0;
-        rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
-    }
-    return rc;
-}
-
-
-/**
- * Handle pending output data/error on stdout or stderr.
- *
- * @return  IPRT status code.
- * @param   hPollSet            The polling set.
- * @param   fPollEvt            The event mask returned by RTPollNoResume.
- * @param   phPipeR             The pipe to be read from.
- * @param   uHandleId           Handle ID of the pipe to be read from.
- * @param   pBuf                Pointer to pipe buffer to store the read data into.
- */
-static int VBoxServiceControlExecProcHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phPipeR,
-                                                       uint32_t uHandleId, PVBOXSERVICECTRLEXECPIPEBUF pBuf)
-{
-    AssertPtrReturn(phPipeR, VERR_INVALID_POINTER);
-    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
-
-    /*
-     * Try drain the pipe before acting on any errors.
-     */
-    int rc = VINF_SUCCESS;
-    size_t  cbRead;
-    uint8_t abBuf[_64K];
-
-    int rc2 = RTPipeRead(*phPipeR, abBuf, sizeof(abBuf), &cbRead);
-    if (RT_SUCCESS(rc2) && cbRead)
-    {
-        uint32_t cbWritten;
-        rc = VBoxServicePipeBufWriteToBuf(pBuf, abBuf,
-                                          cbRead, false /* Pending close */, &cbWritten);
-#ifdef DEBUG_andy
-        VBoxServiceVerbose(4, "ControlExec: Written output event [%u %u], cbRead=%u, cbWritten=%u, rc=%Rrc, uHandleId=%u, fPollEvt=%#x\n",
-                           pBuf->uPID, pBuf->uPipeId, cbRead, cbWritten, rc, uHandleId, fPollEvt);
-#endif
-        if (RT_SUCCESS(rc))
-        {
-            Assert(cbRead == cbWritten);
-            /* Make sure we go another poll round in case there was too much data
-               for the buffer to hold. */
-            fPollEvt &= RTPOLL_EVT_ERROR;
-        }
-    }
-    else if (RT_FAILURE(rc2))
-    {
-        fPollEvt |= RTPOLL_EVT_ERROR;
-        AssertMsg(rc2 == VERR_BROKEN_PIPE, ("%Rrc\n", rc));
-    }
-
-    /*
-     * If an error was signalled, close reading stdout/stderr pipe.
-     */
-    if (fPollEvt & RTPOLL_EVT_ERROR)
-    {
-        rc2 = RTPollSetRemove(hPollSet, uHandleId);
-        AssertRC(rc2);
-
-        rc2 = RTPipeClose(*phPipeR);
-        AssertRC(rc2);
-        *phPipeR = NIL_RTPIPE;
-
-        /* Sinc some error occured (or because the pipe simply broke) we
-         * have to set our pipe buffer to disabled so that others don't wait
-         * for new data to arrive anymore. */
-        VBoxServicePipeBufSetStatus(pBuf, false);
-    }
-    return rc;
-}
-
-
-int VBoxServiceControlExecProcHandleStdInputNotify(RTPOLLSET hPollSet,
-                                                   PRTPIPE phNotificationPipeR, PRTPIPE phInputPipeW)
-{
-#ifdef DEBUG_andy
-    VBoxServiceVerbose(4, "ControlExec: HandleStdInputNotify\n");
-#endif
-    /* Drain the notification pipe. */
-    uint8_t abBuf[8];
-    size_t cbIgnore;
-    int rc = RTPipeRead(*phNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
-    if (RT_SUCCESS(rc))
-    {
-        /*
-         * When the writable handle previously was removed from the poll set we need to add
-         * it here again so that writable events from the started procecss get handled correctly.
-         */
-        RTHANDLE hWritableIgnored;
-        rc = RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE, &hWritableIgnored);
-        if (rc == VERR_POLL_HANDLE_ID_NOT_FOUND)
-            rc = RTPollSetAddPipe(hPollSet, *phInputPipeW, RTPOLL_EVT_WRITE, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
-    }
-    return rc;
-}
-
-
-/**
- * Execution loop which runs in a dedicated per-started-process thread and
- * handles all pipe input/output and signalling stuff.
- *
- * @return  IPRT status code.
- * @param   pThread                     The process' thread handle.
- * @param   hProcess                    The actual process handle.
- * @param   cMsTimeout                  Time limit (in ms) of the process' life time.
- * @param   hPollSet                    The poll set to use.
- * @param   hStdInW                     Handle to the process' stdin write end.
- * @param   hStdOutR                    Handle to the process' stdout read end.
- * @param   hStdErrR                    Handle to the process' stderr read end.
- */
-static int VBoxServiceControlExecProcLoop(PVBOXSERVICECTRLTHREAD pThread,
-                                          RTPROCESS hProcess, RTMSINTERVAL cMsTimeout, RTPOLLSET hPollSet,
-                                          PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR)
-{
-    AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER);
-    AssertPtrReturn(phStdOutR, VERR_INVALID_PARAMETER);
-    AssertPtrReturn(phStdErrR, VERR_INVALID_PARAMETER);
-
-    int                         rc;
-    int                         rc2;
-    uint64_t const              MsStart             = RTTimeMilliTS();
-    RTPROCSTATUS                ProcessStatus       = { 254, RTPROCEXITREASON_ABEND };
-    bool                        fProcessAlive       = true;
-    bool                        fProcessTimedOut    = false;
-    uint64_t                    MsProcessKilled     = UINT64_MAX;
-    RTMSINTERVAL const          cMsPollBase         = *phStdInW != NIL_RTPIPE
-                                                      ? 100   /* Need to poll for input. */
-                                                      : 1000; /* Need only poll for process exit and aborts. */
-    RTMSINTERVAL                cMsPollCur          = 0;
-
-    AssertPtr(pThread);
-    Assert(pThread->enmType == kVBoxServiceCtrlThreadDataExec);
-    PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
-    AssertPtr(pData);
-
-    /*
-     * Assign PID to thread data.
-     * Also check if there already was a thread with the same PID and shut it down -- otherwise
-     * the first (stale) entry will be found and we get really weird results!
-     */
-    rc = VBoxServiceControlExecThreadAssignPID(pData, hProcess);
-    if (RT_FAILURE(rc))
-    {
-        VBoxServiceError("ControlExec: Unable to assign PID to new thread, rc=%Rrc\n", rc);
-        return rc;
-    }
-
-    /*
-     * Before entering the loop, tell the host that we've started the guest
-     * and that it's now OK to send input to the process.
-     */
-    VBoxServiceVerbose(3, "ControlExec: [PID %u]: Process started, CID=%u, User=%s\n",
-                       pData->uPID, pThread->uContextID, pData->pszUser);
-    rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
-                                         pData->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
-                                         NULL /* pvData */, 0 /* cbData */);
-
-    /*
-     * Process input, output, the test pipe and client requests.
-     */
-    while (   RT_SUCCESS(rc)
-           && RT_UNLIKELY(!pThread->fShutdown))
-    {
-        /*
-         * Wait/Process all pending events.
-         */
-        uint32_t idPollHnd;
-        uint32_t fPollEvt;
-        rc2 = RTPollNoResume(hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
-        if (pThread->fShutdown)
-            continue;
-
-        cMsPollCur = 0; /* No rest until we've checked everything. */
-
-        if (RT_SUCCESS(rc2))
-        {
-            /*VBoxServiceVerbose(4, "ControlExec: [PID %u}: RTPollNoResume idPollHnd=%u\n",
-                                 pData->uPID, idPollHnd);*/
-            switch (idPollHnd)
-            {
-                case VBOXSERVICECTRLPIPEID_STDIN_ERROR:
-                    rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, &pData->stdIn);
-                    break;
-
-                case VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY:
-                    rc = VBoxServiceControlExecProcHandleStdInputNotify(hPollSet,
-                                                                        &pData->stdIn.hNotificationPipeR, &pData->pipeStdInW);
-                    AssertRC(rc);
-                    /* Fall through. */
-                case VBOXSERVICECTRLPIPEID_STDIN_WRITABLE:
-                {
-                    size_t cbWritten;
-                    rc = VBoxServiceControlExecProcHandleStdInWritableEvent(hPollSet, fPollEvt, phStdInW,
-                                                                            &pData->stdIn, &cbWritten);
-                    break;
-                }
-
-                case VBOXSERVICECTRLPIPEID_STDOUT:
-#ifdef DEBUG
-                    VBoxServiceVerbose(4, "ControlExec: [PID %u]: StdOut fPollEvt=%#x\n",
-                                       pData->uPID, fPollEvt);
-#endif
-                    rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, phStdOutR,
-                                                                     VBOXSERVICECTRLPIPEID_STDOUT, &pData->stdOut);
-                    break;
-
-                case VBOXSERVICECTRLPIPEID_STDERR:
-#ifdef DEBUG
-                    VBoxServiceVerbose(4, "ControlExec: [PID %u]: StdErr: fPollEvt=%#x\n",
-                                       pData->uPID, fPollEvt);
-#endif
-                    rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt, phStdErrR,
-                                                                     VBOXSERVICECTRLPIPEID_STDERR, &pData->stdErr);
-                    break;
-
-                default:
-                    AssertMsgFailed(("PID=%u idPollHnd=%u fPollEvt=%#x\n",
-                                     pData->uPID, idPollHnd, fPollEvt));
-                    break;
-            }
-            if (RT_FAILURE(rc) || rc == VINF_EOF)
-                break; /* Abort command, or client dead or something. */
-            continue;
-        }
-
-        /*
-         * Check for process death.
-         */
-        if (fProcessAlive)
-        {
-            rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
-            if (RT_SUCCESS_NP(rc2))
-            {
-                fProcessAlive = false;
-                continue;
-            }
-            if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
-                continue;
-            if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
-            {
-                fProcessAlive = false;
-                ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
-                ProcessStatus.iStatus   = 255;
-                AssertFailed();
-            }
-            else
-                AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
-        }
-
-        /*
-         * If the process has terminated, we're should head out.
-         */
-        if (!fProcessAlive)
-            break;
-
-        /*
-         * Check for timed out, killing the process.
-         */
-        uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
-        if (cMsTimeout != RT_INDEFINITE_WAIT)
-        {
-            uint64_t u64Now = RTTimeMilliTS();
-            uint64_t cMsElapsed = u64Now - MsStart;
-            if (cMsElapsed >= cMsTimeout)
-            {
-                VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out (%ums elapsed > %ums timeout), killing ...",
-                                   pData->uPID, cMsElapsed, cMsTimeout);
-
-                fProcessTimedOut = true;
-                if (    MsProcessKilled == UINT64_MAX
-                    ||  u64Now - MsProcessKilled > 1000)
-                {
-                    if (u64Now - MsProcessKilled > 20*60*1000)
-                        break; /* Give up after 20 mins. */
-                    RTProcTerminate(hProcess);
-                    MsProcessKilled = u64Now;
-                    continue;
-                }
-                cMilliesLeft = 10000;
-            }
-            else
-                cMilliesLeft = cMsTimeout - (uint32_t)cMsElapsed;
-        }
-
-        /* Reset the polling interval since we've done all pending work. */
-        cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
-
-        /*
-         * Need to exit?
-         */
-        if (pThread->fShutdown)
-            break;
-    }
-
-    /*
-     * Try kill the process if it's still alive at this point.
-     */
-    if (fProcessAlive)
-    {
-        if (MsProcessKilled == UINT64_MAX)
-        {
-            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Is still alive and not killed yet\n",
-                               pData->uPID);
-
-            MsProcessKilled = RTTimeMilliTS();
-            RTProcTerminate(hProcess);
-            RTThreadSleep(500);
-        }
-
-        for (size_t i = 0; i < 10; i++)
-        {
-            VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Waiting to exit ...\n",
-                               pData->uPID, i + 1);
-            rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
-            if (RT_SUCCESS(rc2))
-            {
-                VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Exited\n",
-                                   pData->uPID, i + 1);
-                fProcessAlive = false;
-                break;
-            }
-            if (i >= 5)
-            {
-                VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Trying to terminate ...\n",
-                                   pData->uPID, i + 1);
-                RTProcTerminate(hProcess);
-            }
-            RTThreadSleep(i >= 5 ? 2000 : 500);
-        }
-
-        if (fProcessAlive)
-            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Could not be killed\n", pData->uPID);
-    }
-
-    /*
-     * If we don't have a client problem (RT_FAILURE(rc)) we'll reply to the
-     * clients exec packet now.
-     */
-    if (RT_SUCCESS(rc))
-    {
-        /* Mark this thread as stopped and do some action required for stopping ... */
-        VBoxServiceControlExecThreadStop(pThread);
-
-        uint32_t uStatus = PROC_STS_UNDEFINED;
-        uint32_t uFlags = 0;
-
-        if (     fProcessTimedOut  && !fProcessAlive && MsProcessKilled != UINT64_MAX)
-        {
-            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out and got killed\n",
-                               pData->uPID);
-            uStatus = PROC_STS_TOK;
-        }
-        else if (fProcessTimedOut  &&  fProcessAlive && MsProcessKilled != UINT64_MAX)
-        {
-            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out and did *not* get killed\n",
-                               pData->uPID);
-            uStatus = PROC_STS_TOA;
-        }
-        else if (pThread->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
-        {
-            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Got terminated because system/service is about to shutdown\n",
-                               pData->uPID);
-            uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
-            uFlags = pData->uFlags; /* Return handed-in execution flags back to the host. */
-        }
-        else if (fProcessAlive)
-        {
-            VBoxServiceError("ControlExec: [PID %u]: Is alive when it should not!\n",
-                             pData->uPID);
-        }
-        else if (MsProcessKilled != UINT64_MAX)
-        {
-            VBoxServiceError("ControlExec: [PID %u]: Has been killed when it should not!\n",
-                             pData->uPID);
-        }
-        else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
-        {
-            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_NORMAL (%u)\n",
-                               pData->uPID, ProcessStatus.iStatus);
-
-            uStatus = PROC_STS_TEN;
-            uFlags = ProcessStatus.iStatus;
-        }
-        else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
-        {
-            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_SIGNAL (%u)\n",
-                               pData->uPID, ProcessStatus.iStatus);
-
-            uStatus = PROC_STS_TES;
-            uFlags = ProcessStatus.iStatus;
-        }
-        else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
-        {
-            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_ABEND (%u)\n",
-                               pData->uPID, ProcessStatus.iStatus);
-
-            uStatus = PROC_STS_TEA;
-            uFlags = ProcessStatus.iStatus;
-        }
-        else
-            VBoxServiceError("ControlExec: [PID %u]: Reached an undefined state!\n",
-                             pData->uPID);
-
-        VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended, CID=%u, Status=%u, Flags=%u\n",
-                           pData->uPID, pThread->uContextID, uStatus, uFlags);
-        rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
-                                             pData->uPID, uStatus, uFlags,
-                                             NULL /* pvData */, 0 /* cbData */);
-        VBoxServiceVerbose(3, "ControlExec: [PID %u]: Process loop ended with rc=%Rrc\n",
-                           pData->uPID, rc);
-
-        /*
-         * Dump stdout for debugging purposes.
-         * Only do that on *very* high verbosity (5+).
-         */
-        if (g_cVerbosity >= 5)
-        {
-            VBoxServiceVerbose(5, "[PID %u]: StdOut:\n", pData->uPID);
-
-            uint8_t szBuf[_64K];
-            uint32_t cbOffset = 0;
-            uint32_t cbRead, cbLeft;
-            while (   RT_SUCCESS(VBoxServicePipeBufPeek(&pData->stdOut, szBuf, sizeof(szBuf),
-                                                        cbOffset, &cbRead, &cbLeft))
-                   && cbRead)
-            {
-#ifdef DEBUG
-                rc2 = RTCritSectEnter(&g_csLog);
-                if (RT_SUCCESS(rc2))
-                {
-#endif
-                    RTStrmWriteEx(g_pStdOut, szBuf, cbRead, NULL /* No partial write. */);
-#ifdef DEBUG
-                    rc2 = RTCritSectLeave(&g_csLog);
-                    if (RT_SUCCESS(rc))
-                        rc = rc2;
-                }
-#endif
-                cbOffset += cbRead;
-                if (!cbLeft)
-                    break;
-            }
-
-            VBoxServiceVerbose(5, "\n");
-        }
-    }
-    else
-        VBoxServiceError("ControlExec: [PID %u]: Loop failed with rc=%Rrc\n",
-                         pData->uPID, rc);
-    return rc;
-}
-
-
-/**
- * Sets up the redirection / pipe / nothing for one of the standard handles.
- *
- * @returns IPRT status code.  No client replies made.
- * @param   fd                  Which standard handle it is (0 == stdin, 1 ==
- *                              stdout, 2 == stderr).
- * @param   ph                  The generic handle that @a pph may be set
- *                              pointing to.  Always set.
- * @param   pph                 Pointer to the RTProcCreateExec argument.
- *                              Always set.
- * @param   phPipe              Where to return the end of the pipe that we
- *                              should service.  Always set.
- */
-static int VBoxServiceControlExecSetupPipe(int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
-{
-    AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
-    AssertPtrReturn(pph, VERR_INVALID_PARAMETER);
-    AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
-
-    ph->enmType = RTHANDLETYPE_PIPE;
-    ph->u.hPipe = NIL_RTPIPE;
-    *pph        = NULL;
-    *phPipe     = NIL_RTPIPE;
-
-    int rc;
-
-    /*
-     * Setup a pipe for forwarding to/from the client.
-     * The ph union struct will be filled with a pipe read/write handle
-     * to represent the "other" end to phPipe.
-     */
-    if (fd == 0) /* stdin? */
-    {
-        /* Connect a wrtie pipe specified by phPipe to stdin. */
-        rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
-    }
-    else /* stdout or stderr? */
-    {
-        /* Connect a read pipe specified by phPipe to stdout or stderr. */
-        rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
-    }
-    if (RT_FAILURE(rc))
-        return rc;
-    ph->enmType = RTHANDLETYPE_PIPE;
-    *pph = ph;
-
-    return rc;
-}
-
-
-/**
- * Expands a file name / path to its real content. This only works on Windows
- * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting
- * with system / administrative rights).
- *
- * @return  IPRT status code.
- * @param   pszPath                     Path to resolve.
- * @param   pszExpanded                 Pointer to string to store the resolved path in.
- * @param   cbExpanded                  Size (in bytes) of string to store the resolved path.
- */
-static int VBoxServiceControlExecMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded)
-{
-    int rc = VINF_SUCCESS;
-#ifdef RT_OS_WINDOWS
-    if (!ExpandEnvironmentStrings(pszPath, pszExpanded, cbExpanded))
-        rc = RTErrConvertFromWin32(GetLastError());
-#else
-    /* No expansion for non-Windows yet. */
-    rc = RTStrCopy(pszExpanded, cbExpanded, pszPath);
-#endif
-#ifdef DEBUG
-    VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecMakeFullPath: %s -> %s\n",
-                       pszPath, pszExpanded);
-#endif
-    return rc;
-}
-
-
-/**
- * Resolves the full path of a specified executable name. This function also
- * resolves internal VBoxService tools to its appropriate executable path + name.
- *
- * @return  IPRT status code.
- * @param   pszFileName                 File name to resovle.
- * @param   pszResolved                 Pointer to a string where the resolved file name will be stored.
- * @param   cbResolved                  Size (in bytes) of resolved file name string.
- */
-static int VBoxServiceControlExecResolveExecutable(const char *pszFileName, char *pszResolved, size_t cbResolved)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Search the path of our executable. */
-    char szVBoxService[RTPATH_MAX];
-    if (RTProcGetExecutablePath(szVBoxService, sizeof(szVBoxService)))
-    {
-        char *pszExecResolved = NULL;
-        if (   (g_pszProgName && RTStrICmp(pszFileName, g_pszProgName) == 0)
-            || !RTStrICmp(pszFileName, VBOXSERVICE_NAME))
-        {
-            /* We just want to execute VBoxService (no toolbox). */
-            pszExecResolved = RTStrDup(szVBoxService);
-        }
-        else /* Nothing to resolve, copy original. */
-            pszExecResolved = RTStrDup(pszFileName);
-        AssertPtr(pszExecResolved);
-
-        rc = VBoxServiceControlExecMakeFullPath(pszExecResolved, pszResolved, cbResolved);
-#ifdef DEBUG
-        VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecResolveExecutable: %s -> %s\n",
-                           pszFileName, pszResolved);
-#endif
-        RTStrFree(pszExecResolved);
-    }
-    return rc;
-}
-
-
-/**
- * Constructs the argv command line by resolving environment variables
- * and relative paths.
- *
- * @return IPRT status code.
- * @param  pszArgv0         First argument (argv0), either original or modified version.
- * @param  papszArgs        Original argv command line from the host, starting at argv[1].
- * @param  ppapszArgv       Pointer to a pointer with the new argv command line.
- *                          Needs to be freed with RTGetOptArgvFree.
- */
-static int VBoxServiceControlExecPrepareArgv(const char *pszArgv0,
-                                             const char * const *papszArgs, char ***ppapszArgv)
-{
-/** @todo RTGetOptArgvToString converts to MSC quoted string, while
- *        RTGetOptArgvFromString takes bourne shell according to the docs...
- * Actually, converting to and from here is a very roundabout way of prepending
- * an entry (pszFilename) to an array (*ppapszArgv). */
-    int rc = VINF_SUCCESS;
-    char *pszNewArgs = NULL;
-    if (pszArgv0)
-        rc = RTStrAAppend(&pszNewArgs, pszArgv0);
-    if (   RT_SUCCESS(rc)
-        && papszArgs)
-
-    {
-        char *pszArgs;
-        rc = RTGetOptArgvToString(&pszArgs, papszArgs,
-                                  RTGETOPTARGV_CNV_QUOTE_MS_CRT); /* RTGETOPTARGV_CNV_QUOTE_BOURNE_SH */
-        if (RT_SUCCESS(rc))
-        {
-            rc = RTStrAAppend(&pszNewArgs, " ");
-            if (RT_SUCCESS(rc))
-                rc = RTStrAAppend(&pszNewArgs, pszArgs);
-        }
-    }
-
-    if (RT_SUCCESS(rc))
-    {
-        int iNumArgsIgnored;
-        rc = RTGetOptArgvFromString(ppapszArgv, &iNumArgsIgnored,
-                                    pszNewArgs ? pszNewArgs : "", NULL /* Use standard separators. */);
-    }
-
-    if (pszNewArgs)
-        RTStrFree(pszNewArgs);
-    return rc;
-}
-
-
-/**
- * Helper function to create/start a process on the guest.
- *
- * @return  IPRT status code.
- * @param   pszExec                     Full qualified path of process to start (without arguments).
- * @param   papszArgs                   Pointer to array of command line arguments.
- * @param   hEnv                        Handle to environment block to use.
- * @param   fFlags                      Process execution flags.
- * @param   phStdIn                     Handle for the process' stdin pipe.
- * @param   phStdOut                    Handle for the process' stdout pipe.
- * @param   phStdErr                    Handle for the process' stderr pipe.
- * @param   pszAsUser                   User name (account) to start the process under.
- * @param   pszPassword                 Password of the specified user.
- * @param   phProcess                   Pointer which will receive the process handle after
- *                                      successful process start.
- */
-static int VBoxServiceControlExecCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
-                                               PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
-                                               const char *pszPassword, PRTPROCESS phProcess)
-{
-    AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER);
-    AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
-    AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER);
-
-    int  rc = VINF_SUCCESS;
-    char szExecExp[RTPATH_MAX];
-#ifdef RT_OS_WINDOWS
-    /*
-     * If sysprep should be executed do this in the context of VBoxService, which
-     * (usually, if started by SCM) has administrator rights. Because of that a UI
-     * won't be shown (doesn't have a desktop).
-     */
-    if (RTStrICmp(pszExec, "sysprep") == 0)
-    {
-        /* Use a predefined sysprep path as default. */
-        char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
-
-        /*
-         * On Windows Vista (and up) sysprep is located in "system32\\sysprep\\sysprep.exe",
-         * so detect the OS and use a different path.
-         */
-        OSVERSIONINFOEX OSInfoEx;
-        RT_ZERO(OSInfoEx);
-        OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
-        if (    GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
-            &&  OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
-            &&  OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
-        {
-            rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
-            if (RT_SUCCESS(rc))
-                rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\sysprep\\sysprep.exe");
-        }
-
-        if (RT_SUCCESS(rc))
-        {
-            char **papszArgsExp;
-            rc = VBoxServiceControlExecPrepareArgv(szSysprepCmd /* argv0 */, papszArgs, &papszArgsExp);
-            if (RT_SUCCESS(rc))
-            {
-                rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */,
-                                    phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
-                                    NULL /* pszPassword */, phProcess);
-            }
-            RTGetOptArgvFree(papszArgsExp);
-        }
-        return rc;
-    }
-#endif /* RT_OS_WINDOWS */
-
-#ifdef VBOXSERVICE_TOOLBOX
-    if (RTStrStr(pszExec, "vbox_") == pszExec)
-    {
-        /* We want to use the internal toolbox (all internal
-         * tools are starting with "vbox_" (e.g. "vbox_cat"). */
-        rc = VBoxServiceControlExecResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp));
-    }
-    else
-    {
-#endif
-        /*
-         * Do the environment variables expansion on executable and arguments.
-         */
-        rc = VBoxServiceControlExecResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
-#ifdef VBOXSERVICE_TOOLBOX
-    }
-#endif
-    if (RT_SUCCESS(rc))
-    {
-        char **papszArgsExp;
-        rc = VBoxServiceControlExecPrepareArgv(pszExec /* Always use the unmodified executable name as argv0. */,
-                                               papszArgs /* Append the rest of the argument vector (if any). */, &papszArgsExp);
-        if (RT_SUCCESS(rc))
-        {
-            uint32_t uProcFlags = 0;
-            if (fFlags)
-            {
-                /* Process Main flag "ExecuteProcessFlag_Hidden". */
-                if (fFlags & RT_BIT(2))
-                    uProcFlags |= RTPROC_FLAGS_HIDDEN;
-                /* Process Main flag "ExecuteProcessFlag_NoProfile". */
-                if (fFlags & RT_BIT(3))
-                    uProcFlags |= RTPROC_FLAGS_NO_PROFILE;
-            }
-
-            /* If no user name specified run with current credentials (e.g.
-             * full service/system rights). This is prohibited via official Main API!
-             *
-             * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication
-             * code (at least on Windows) for running processes as different users
-             * started from our system service. */
-            if (*pszAsUser)
-                uProcFlags |= RTPROC_FLAGS_SERVICE;
-#ifdef DEBUG
-            VBoxServiceVerbose(3, "ControlExec: Command: %s\n", szExecExp);
-            for (size_t i = 0; papszArgsExp[i]; i++)
-                VBoxServiceVerbose(3, "ControlExec:\targv[%ld]: %s\n", i, papszArgsExp[i]);
-#endif
-            /* Do normal execution. */
-            rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags,
-                                phStdIn, phStdOut, phStdErr,
-                                *pszAsUser   ? pszAsUser   : NULL,
-                                *pszPassword ? pszPassword : NULL,
-                                phProcess);
-            RTGetOptArgvFree(papszArgsExp);
-        }
-    }
-    return rc;
-}
-
-/**
- * The actual worker routine (lopp) for a started guest process.
- *
- * @return  IPRT status code.
- * @param   PVBOXSERVICECTRLTHREAD         Thread data associated with a started process.
- */
-static DECLCALLBACK(int) VBoxServiceControlExecProcessWorker(PVBOXSERVICECTRLTHREAD pThread)
-{
-    AssertPtr(pThread);
-    PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
-    AssertPtr(pData);
-
-    VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" started\n", pData->pszCmd);
-
-    int rc = VbglR3GuestCtrlConnect(&pThread->uClientID);
-    if (RT_FAILURE(rc))
-    {
-        VBoxServiceError("ControlExec: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
-        RTThreadUserSignal(RTThreadSelf());
-        return rc;
-    }
-
-    bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
-
-    /*
-     * Create the environment.
-     */
-    RTENV hEnv;
-    rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
-    if (RT_SUCCESS(rc))
-    {
-        size_t i;
-        for (i = 0; i < pData->uNumEnvVars && pData->papszEnv; i++)
-        {
-            rc = RTEnvPutEx(hEnv, pData->papszEnv[i]);
-            if (RT_FAILURE(rc))
-                break;
-        }
-        if (RT_SUCCESS(rc))
-        {
-            /*
-             * Setup the redirection of the standard stuff.
-             */
-            /** @todo consider supporting: gcc stuff.c >file 2>&1.  */
-            RTHANDLE    hStdIn;
-            PRTHANDLE   phStdIn;
-            rc = VBoxServiceControlExecSetupPipe(0 /*STDIN_FILENO*/, &hStdIn, &phStdIn, &pData->pipeStdInW);
-            if (RT_SUCCESS(rc))
-            {
-                RTHANDLE    hStdOut;
-                PRTHANDLE   phStdOut;
-                RTPIPE      hStdOutR;
-                rc = VBoxServiceControlExecSetupPipe(1 /*STDOUT_FILENO*/, &hStdOut, &phStdOut, &hStdOutR);
-                if (RT_SUCCESS(rc))
-                {
-                    RTHANDLE    hStdErr;
-                    PRTHANDLE   phStdErr;
-                    RTPIPE      hStdErrR;
-                    rc = VBoxServiceControlExecSetupPipe(2 /*STDERR_FILENO*/, &hStdErr, &phStdErr, &hStdErrR);
-                    if (RT_SUCCESS(rc))
-                    {
-                        /*
-                         * Create a poll set for the pipes and let the
-                         * transport layer add stuff to it as well.
-                         */
-                        RTPOLLSET hPollSet;
-                        rc = RTPollSetCreate(&hPollSet);
-                        if (RT_SUCCESS(rc))
-                        {
-                            rc = RTPollSetAddPipe(hPollSet, pData->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN_ERROR);
-                            if (RT_SUCCESS(rc))
-                                rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDOUT);
-                            if (RT_SUCCESS(rc))
-                                rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDERR);
-                            if (RT_SUCCESS(rc))
-                                rc = RTPollSetAddPipe(hPollSet, pData->pipeStdInW, RTPOLL_EVT_WRITE, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE);
-                            if (RT_SUCCESS(rc))
-                                rc = RTPollSetAddPipe(hPollSet, pData->stdIn.hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY);
-                            if (RT_SUCCESS(rc))
-                            {
-                                RTPROCESS hProcess;
-                                rc = VBoxServiceControlExecCreateProcess(pData->pszCmd, pData->papszArgs, hEnv, pData->uFlags,
-                                                                         phStdIn, phStdOut, phStdErr,
-                                                                         pData->pszUser, pData->pszPassword,
-                                                                         &hProcess);
-                                if (RT_FAILURE(rc))
-                                    VBoxServiceError("ControlExec: Error starting process, rc=%Rrc\n", rc);
-                                /*
-                                 * Tell the control thread that it can continue
-                                 * spawning services. This needs to be done after the new
-                                 * process has been started because otherwise signal handling
-                                 * on (Open) Solaris does not work correctly (see #5068).
-                                 */
-                                int rc2 = RTThreadUserSignal(RTThreadSelf());
-                                if (RT_FAILURE(rc2))
-                                    rc = rc2;
-                                fSignalled = true;
-
-                                if (RT_SUCCESS(rc))
-                                {
-                                    /*
-                                     * Close the child ends of any pipes and redirected files.
-                                     */
-                                    rc2 = RTHandleClose(phStdIn);   AssertRC(rc2);
-                                    phStdIn    = NULL;
-                                    rc2 = RTHandleClose(phStdOut);  AssertRC(rc2);
-                                    phStdOut   = NULL;
-                                    rc2 = RTHandleClose(phStdErr);  AssertRC(rc2);
-                                    phStdErr   = NULL;
-
-                                    /* Enter the process loop. */
-                                    rc = VBoxServiceControlExecProcLoop(pThread,
-                                                                        hProcess, pData->uTimeLimitMS, hPollSet,
-                                                                        &pData->pipeStdInW, &hStdOutR, &hStdErrR);
-
-                                    /*
-                                     * The handles that are no longer in the set have
-                                     * been closed by the above call in order to prevent
-                                     * the guest from getting stuck accessing them.
-                                     * So, NIL the handles to avoid closing them again.
-                                     */
-                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_WRITABLE, NULL)))
-                                        pData->pipeStdInW = NIL_RTPIPE;
-                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY, NULL)))
-                                        pData->stdIn.hNotificationPipeR = NIL_RTPIPE;
-                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDOUT, NULL)))
-                                        hStdOutR = NIL_RTPIPE;
-                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDERR, NULL)))
-                                        hStdErrR = NIL_RTPIPE;
-                                }
-                                else /* Something went wrong; report error! */
-                                {
-                                    VBoxServiceError("ControlExec: Could not start process '%s' (CID: %u)! Error: %Rrc\n",
-                                                     pData->pszCmd, pThread->uContextID, rc);
-
-                                    rc2 = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, pData->uPID,
-                                                                          PROC_STS_ERROR, rc,
-                                                                          NULL /* pvData */, 0 /* cbData */);
-                                    if (RT_FAILURE(rc2))
-                                        VBoxServiceError("ControlExec: Could not report process start error! Error: %Rrc (process error %Rrc)\n",
-                                                         rc2, rc);
-                                }
-                            }
-                            RTPollSetDestroy(hPollSet);
-                            RTPipeClose(pData->stdIn.hNotificationPipeR);
-                        }
-                        RTPipeClose(hStdErrR);
-                        RTHandleClose(phStdErr);
-                    }
-                    RTPipeClose(hStdOutR);
-                    RTHandleClose(phStdOut);
-                }
-                RTPipeClose(pData->pipeStdInW);
-                RTHandleClose(phStdIn);
-            }
-        }
-        RTEnvDestroy(hEnv);
-    }
-
-    VbglR3GuestCtrlDisconnect(pThread->uClientID);
-    VBoxServiceVerbose(3, "ControlExec: [PID %u]: Thread of process \"%s\" ended with rc=%Rrc\n",
-                       pData->uPID, pData->pszCmd, rc);
-
-    /*
-     * If something went wrong signal the user event so that others don't wait
-     * forever on this thread.
-     */
-    if (RT_FAILURE(rc) && !fSignalled)
-        RTThreadUserSignal(RTThreadSelf());
-    return rc;
-}
-
-
-/**
- * Thread main routine for a started process.
- *
- * @return IPRT status code.
- * @param  RTTHREAD             Pointer to the thread's data.
- * @param  void*                User-supplied argument pointer.
- *
- */
-static DECLCALLBACK(int) VBoxServiceControlExecThread(RTTHREAD ThreadSelf, void *pvUser)
-{
-    PVBOXSERVICECTRLTHREAD pThread = (VBOXSERVICECTRLTHREAD*)pvUser;
-    AssertPtr(pThread);
-    return VBoxServiceControlExecProcessWorker(pThread);
-}
-
-
-/**
- * Executes (starts) a process on the guest. This causes a new thread to be created
- * so that this function will not block the overall program execution.
- *
- * @return  IPRT status code.
- * @param   uClientID                   Client ID for accessing host service.
- * @param   uContextID                  Context ID to associate the process to start with.
- * @param   pszCmd                      Full qualified path of process to start (without arguments).
- * @param   uFlags                      Process execution flags.
- * @param   pszArgs                     String of arguments to pass to the process to start.
- * @param   uNumArgs                    Number of arguments specified in pszArgs.
- * @param   pszEnv                      String of environment variables ("FOO=BAR") to pass to the process
- *                                      to start.
- * @param   cbEnv                       Size (in bytes) of environment variables.
- * @param   uNumEnvVars                 Number of environment variables specified in pszEnv.
- * @param   pszUser                     User name (account) to start the process under.
- * @param   pszPassword                 Password of specified user name (account).
- * @param   uTimeLimitMS                Time limit (in ms) of the process' life time.
- */
-int VBoxServiceControlExecProcess(uint32_t uClientID, uint32_t uContextID,
-                                  const char *pszCmd, uint32_t uFlags,
-                                  const char *pszArgs, uint32_t uNumArgs,
-                                  const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
-                                  const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS)
-{
-    bool fAllowed = false;
-    int rc = VBoxServiceControlExecThreadStartAllowed(&fAllowed);
-    if (RT_FAILURE(rc))
-        VBoxServiceError("ControlExec: Error determining whether process can be started or not, rc=%Rrc\n", rc);
-
-    if (fAllowed)
-    {
-        /*
-         * Allocate new thread data and assign it to our thread list.
-         */
-        PVBOXSERVICECTRLTHREAD pThread = (PVBOXSERVICECTRLTHREAD)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREAD));
-        if (pThread)
-        {
-            rc = VBoxServiceControlExecThreadAlloc(pThread,
-                                                   uContextID,
-                                                   pszCmd, uFlags,
-                                                   pszArgs, uNumArgs,
-                                                   pszEnv, cbEnv, uNumEnvVars,
-                                                   pszUser, pszPassword,
-                                                   uTimeLimitMS);
-            if (RT_SUCCESS(rc))
-            {
-                static uint32_t uCtrlExecThread = 0;
-                char szThreadName[32];
-                if (!RTStrPrintf(szThreadName, sizeof(szThreadName), "controlexec%ld", uCtrlExecThread++))
-                    AssertMsgFailed(("Unable to create unique control exec thread name!\n"));
-
-                rc = RTThreadCreate(&pThread->Thread, VBoxServiceControlExecThread,
-                                    (void *)(PVBOXSERVICECTRLTHREAD*)pThread, 0,
-                                    RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, szThreadName);
-                if (RT_FAILURE(rc))
-                {
-                    VBoxServiceError("ControlExec: RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n",
-                                     rc, pThread);
-                }
-                else
-                {
-                    VBoxServiceVerbose(4, "ControlExec: Waiting for thread to initialize ...\n");
-
-                    /* Wait for the thread to initialize. */
-                    RTThreadUserWait(pThread->Thread, 60 * 1000 /* 60 seconds max. */);
-                    if (pThread->fShutdown)
-                    {
-                        VBoxServiceError("ControlExec: Thread for process \"%s\" failed to start!\n", pszCmd);
-                        rc = VERR_GENERAL_FAILURE;
-                    }
-                    else
-                    {
-                        pThread->fStarted = true;
-                        /*rc =*/ RTListAppend(&g_GuestControlThreads, &pThread->Node);
-                    }
-                }
-
-                if (RT_FAILURE(rc))
-                    VBoxServiceControlExecThreadDataDestroy((PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData);
-            }
-            if (RT_FAILURE(rc))
-                RTMemFree(pThread);
-        }
-        else
-            rc = VERR_NO_MEMORY;
-    }
-    else /* Process start is not allowed due to policy settings. */
-    {
-        VBoxServiceVerbose(3, "ControlExec: Guest process limit is reached!\n");
-
-        /* Tell the host. */
-        rc = VbglR3GuestCtrlExecReportStatus(uClientID, uContextID, 0 /* PID */,
-                                             PROC_STS_ERROR, VERR_MAX_PROCS_REACHED,
-                                             NULL /* pvData */, 0 /* cbData */);
-    }
-    return rc;
-}
-
-
-/**
- * Handles starting processes on the guest.
- *
- * @returns IPRT status code.
- * @param   u32ClientId                 The HGCM client session ID.
- * @param   uNumParms                   The number of parameters the host is offering.
- */
-int VBoxServiceControlExecHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms)
-{
-    uint32_t uContextID;
-    char szCmd[_1K];
-    uint32_t uFlags;
-    char szArgs[_1K];
-    uint32_t uNumArgs;
-    char szEnv[_64K];
-    uint32_t cbEnv = sizeof(szEnv);
-    uint32_t uNumEnvVars;
-    char szUser[128];
-    char szPassword[128];
-    uint32_t uTimeLimitMS;
-
-#if 0 /* for valgrind */
-    RT_ZERO(szCmd);
-    RT_ZERO(szArgs);
-    RT_ZERO(szEnv);
-    RT_ZERO(szUser);
-    RT_ZERO(szPassword);
-#endif
-
-    if (uNumParms != 11)
-        return VERR_INVALID_PARAMETER;
-
-    int rc = VbglR3GuestCtrlExecGetHostCmd(u32ClientId,
-                                           uNumParms,
-                                           &uContextID,
-                                           /* Command */
-                                           szCmd,      sizeof(szCmd),
-                                           /* Flags */
-                                           &uFlags,
-                                           /* Arguments */
-                                           szArgs,     sizeof(szArgs), &uNumArgs,
-                                           /* Environment */
-                                           szEnv, &cbEnv, &uNumEnvVars,
-                                           /* Credentials */
-                                           szUser,     sizeof(szUser),
-                                           szPassword, sizeof(szPassword),
-                                           /* Timelimit */
-                                           &uTimeLimitMS);
-#ifdef DEBUG
-    VBoxServiceVerbose(3, "ControlExec: Start process szCmd=%s, uFlags=%u, szArgs=%s, szEnv=%s, szUser=%s, szPW=%s, uTimeout=%u\n",
-                       szCmd, uFlags, uNumArgs ? szArgs : "<None>", uNumEnvVars ? szEnv : "<None>", szUser, szPassword, uTimeLimitMS);
-#endif
-    if (RT_SUCCESS(rc))
-    {
-        /** @todo Put the following params into a struct! */
-        rc = VBoxServiceControlExecProcess(u32ClientId, uContextID,
-                                           szCmd, uFlags, szArgs, uNumArgs,
-                                           szEnv, cbEnv, uNumEnvVars,
-                                           szUser, szPassword, uTimeLimitMS);
-    }
-    else
-        VBoxServiceError("ControlExec: Failed to retrieve exec start command! Error: %Rrc\n", rc);
-    return rc;
-}
-
-
-/**
- * Handles input for a started process by copying the received data into its
- * stdin pipe.
- *
- * @returns IPRT status code.
- * @param   u32ClientId                 The HGCM client session ID.
- * @param   uNumParms                   The number of parameters the host is offering.
- * @param   cMaxBufSize                 The maximum buffer size for retrieving the input data.
- */
-int VBoxServiceControlExecHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize)
-{
-    uint32_t uContextID;
-    uint32_t uPID;
-    uint32_t uFlags;
-    uint32_t cbSize;
-
-    AssertReturn(RT_IS_POWER_OF_TWO(cbMaxBufSize), VERR_INVALID_PARAMETER);
-    uint8_t *pabBuffer = (uint8_t*)RTMemAlloc(cbMaxBufSize);
-    AssertPtrReturn(pabBuffer, VERR_NO_MEMORY);
-
-    uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status sent back to the host. */
-    uint32_t cbWritten = 0; /* Number of bytes written to the guest. */
-
-    /*
-     * Ask the host for the input data.
-     */
-    int rc = VbglR3GuestCtrlExecGetHostCmdInput(u32ClientId, uNumParms,
-                                                &uContextID, &uPID, &uFlags,
-                                                pabBuffer, cbMaxBufSize, &cbSize);
-    if (RT_FAILURE(rc))
-    {
-        VBoxServiceError("ControlExec: [PID %u]: Failed to retrieve exec input command! Error: %Rrc\n",
-                         uPID, rc);
-    }
-    else if (cbSize >  cbMaxBufSize)
-    {
-        VBoxServiceError("ControlExec: [PID %u]: Maximum input buffer size is too small! cbSize=%u, cbMaxBufSize=%u\n",
-                         uPID, cbSize, cbMaxBufSize);
-        rc = VERR_INVALID_PARAMETER;
-    }
-    else
-    {
-        /*
-         * Is this the last input block we need to deliver? Then let the pipe know ...
-         */
-        bool fPendingClose = false;
-        if (uFlags & INPUT_FLAG_EOF)
-        {
-            fPendingClose = true;
-            VBoxServiceVerbose(4, "ControlExec: [PID %u]: Got last input block of size %u ...\n",
-                               uPID, cbSize);
-        }
-
-        rc = VBoxServiceControlExecThreadSetInput(uPID, fPendingClose, pabBuffer,
-                                                  cbSize, &cbWritten);
-        VBoxServiceVerbose(4, "ControlExec: [PID %u]: Written input, rc=%Rrc, uFlags=0x%x, fPendingClose=%d, cbSize=%u, cbWritten=%u\n",
-                           uPID, rc, uFlags, fPendingClose, cbSize, cbWritten);
-        if (RT_SUCCESS(rc))
-        {
-            if (cbWritten || !cbSize) /* Did we write something or was there anything to write at all? */
-            {
-                uStatus = INPUT_STS_WRITTEN;
-                uFlags = 0;
-            }
-        }
-        else
-        {
-            if (rc == VERR_BAD_PIPE)
-                uStatus = INPUT_STS_TERMINATED;
-            else if (rc == VERR_BUFFER_OVERFLOW)
-                uStatus = INPUT_STS_OVERFLOW;
-        }
-    }
-    RTMemFree(pabBuffer);
-
-    /*
-     * If there was an error and we did not set the host status
-     * yet, then do it now.
-     */
-    if (   RT_FAILURE(rc)
-        && uStatus == INPUT_STS_UNDEFINED)
-    {
-        uStatus = INPUT_STS_ERROR;
-        uFlags = rc;
-    }
-    Assert(uStatus > INPUT_STS_UNDEFINED);
-
-    VBoxServiceVerbose(3, "ControlExec: [PID %u]: Input processed, CID=%u, uStatus=%u, uFlags=0x%x, cbWritten=%u\n",
-                       uPID, uContextID, uStatus, uFlags, cbWritten);
-
-    /* Note: Since the context ID is unique the request *has* to be completed here,
-     *       regardless whether we got data or not! Otherwise the progress object
-     *       on the host never will get completed! */
-    rc = VbglR3GuestCtrlExecReportStatusIn(u32ClientId, uContextID, uPID,
-                                           uStatus, uFlags, (uint32_t)cbWritten);
-
-    if (RT_FAILURE(rc))
-        VBoxServiceError("ControlExec: [PID %u]: Failed to report input status! Error: %Rrc\n",
-                         uPID, rc);
-    return rc;
-}
-
-
-/**
- * Handles the guest control output command.
- *
- * @return  IPRT status code.
- * @param   u32ClientId     idClient    The HGCM client session ID.
- * @param   uNumParms       cParms      The number of parameters the host is
- *                                      offering.
- */
-int VBoxServiceControlExecHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms)
-{
-    uint32_t uContextID;
-    uint32_t uPID;
-    uint32_t uHandleID;
-    uint32_t uFlags;
-
-    int rc = VbglR3GuestCtrlExecGetHostCmdOutput(u32ClientId, uNumParms,
-                                                 &uContextID, &uPID, &uHandleID, &uFlags);
-    if (RT_SUCCESS(rc))
-    {
-        uint32_t cbRead = 0;
-        uint8_t *pBuf = (uint8_t*)RTMemAlloc(_64K);
-        if (pBuf)
-        {
-            rc = VBoxServiceControlExecThreadGetOutput(uPID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */,
-                                                       pBuf, _64K /* cbSize */, &cbRead);
-            if (RT_SUCCESS(rc))
-                VBoxServiceVerbose(3, "ControlExec: [PID %u]: Got output, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u\n",
-                                   uPID, uContextID, cbRead, uHandleID, uFlags);
-            else
-                VBoxServiceError("ControlExec: [PID %u]: Failed to retrieve output, CID=%u, uHandle=%u, rc=%Rrc\n",
-                                 uPID, uContextID, uHandleID, rc);
-            /* Note: Since the context ID is unique the request *has* to be completed here,
-             *       regardless whether we got data or not! Otherwise the progress object
-             *       on the host never will get completed! */
-            /* cbRead now contains actual size. */
-            int rc2 = VbglR3GuestCtrlExecSendOut(u32ClientId, uContextID, uPID, uHandleID, uFlags,
-                                                 pBuf, cbRead);
-            if (RT_SUCCESS(rc))
-                rc = rc2;
-            RTMemFree(pBuf);
-        }
-        else
-            rc = VERR_NO_MEMORY;
-    }
-
-    if (RT_FAILURE(rc))
-        VBoxServiceError("ControlExec: [PID %u]: Failed to handle output command! Error: %Rrc\n",
-                         uPID, rc);
-    return rc;
-}
-
Index: trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.cpp
===================================================================
--- trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.cpp	(revision 39278)
+++ trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.cpp	(revision 39279)
@@ -1,5 +1,5 @@
 /* $Id$ */
 /** @file
- * VBoxServiceControlExecThread - Thread for an executed guest process.
+ * VBoxServiceControlExecThread - Thread for every started guest process.
  */
 
@@ -22,21 +22,28 @@
 #include <iprt/asm.h>
 #include <iprt/assert.h>
+#include <iprt/env.h>
 #include <iprt/getopt.h>
+#include <iprt/handle.h>
 #include <iprt/mem.h>
+#include <iprt/path.h>
 #include <iprt/pipe.h>
+#include <iprt/poll.h>
+#include <iprt/process.h>
 #include <iprt/semaphore.h>
 #include <iprt/string.h>
-
+#include <iprt/thread.h>
+
+#include <VBox/VBoxGuestLib.h>
 #include <VBox/HostServices/GuestControlSvc.h>
 
-#include "VBoxServicePipeBuf.h"
+#include "VBoxServiceInternal.h"
 #include "VBoxServiceControlExecThread.h"
 
-extern uint32_t g_GuestControlProcsMaxKept;
-extern RTLISTNODE g_GuestControlThreads;
-extern RTCRITSECT g_GuestControlThreadsCritSect;
-
-PVBOXSERVICECTRLTHREAD vboxServiceControlExecThreadGetByPID(uint32_t uPID);
-int VBoxServiceControlExecThreadShutdown(const PVBOXSERVICECTRLTHREAD pThread);
+using namespace guestControl;
+
+/* Internal functions. */
+int vboxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREAD pData, uint32_t uPID);
+void vboxServiceControlExecThreadFree(PVBOXSERVICECTRLTHREAD pData);
+int vboxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread);
 
 /**
@@ -73,35 +80,40 @@
 
     pThread->fShutdown = false;
-    pThread->fStarted = false;
-    pThread->fStopped = false;
+    pThread->fStarted  = false;
+    pThread->fStopped  = false;
 
     pThread->uContextID = u32ContextID;
     /* ClientID will be assigned when thread is started! */
 
-    /* Specific stuff. */
-    PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREADDATAEXEC));
-    if (pData == NULL)
-        return VERR_NO_MEMORY;
-
-    pData->uPID = 0; /* Don't have a PID yet. */
-    pData->pszCmd = RTStrDup(pszCmd);
-    pData->uFlags = uFlags;
-    pData->uNumEnvVars = 0;
-    pData->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
+    int rc = RTCritSectInit(&pThread->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = RTSemEventMultiCreate(&pThread->RequestEvent);
+    AssertRCReturn(rc, rc);
+
+    pThread->uPID = 0; /* Don't have a PID yet. */
+    pThread->pszCmd = RTStrDup(pszCmd);
+    pThread->uFlags = uFlags;
+    pThread->uTimeLimitMS = (   uTimeLimitMS == UINT32_MAX
+                             || uTimeLimitMS == 0)
+                          ? RT_INDEFINITE_WAIT : uTimeLimitMS;
 
     /* Prepare argument list. */
-    int rc = RTGetOptArgvFromString(&pData->papszArgs, (int*)&pData->uNumArgs,
-                                    (uNumArgs > 0) ? pszArgs : "", NULL);
+    pThread->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
+    rc = RTGetOptArgvFromString(&pThread->papszArgs, (int*)&pThread->uNumArgs,
+                                (uNumArgs > 0) ? pszArgs : "", NULL);
     /* Did we get the same result? */
-    Assert(uNumArgs == pData->uNumArgs);
+    Assert(uNumArgs == pThread->uNumArgs);
 
     if (RT_SUCCESS(rc))
     {
         /* Prepare environment list. */
+        pThread->uNumEnvVars = 0;
         if (uNumEnvVars)
         {
-            pData->papszEnv = (char **)RTMemAlloc(uNumEnvVars * sizeof(char*));
-            AssertPtr(pData->papszEnv);
-            pData->uNumEnvVars = uNumEnvVars;
+            pThread->papszEnv = (char **)RTMemAlloc(uNumEnvVars * sizeof(char*));
+            AssertPtr(pThread->papszEnv);
+            pThread->uNumEnvVars = uNumEnvVars;
 
             const char *pszCur = pszEnv;
@@ -116,5 +128,5 @@
                     break;
                 }
-                int cbStr = RTStrAPrintf(&pData->papszEnv[i++], "%s", pszCur);
+                int cbStr = RTStrAPrintf(&pThread->papszEnv[i++], "%s", pszCur);
                 if (cbStr < 0)
                 {
@@ -127,34 +139,1196 @@
         }
 
-        pData->pszUser = RTStrDup(pszUser);
-        pData->pszPassword = RTStrDup(pszPassword);
-        pData->uTimeLimitMS = uTimeLimitMS;
-
-        /* Adjust time limit value. */
-        pData->uTimeLimitMS = (   uTimeLimitMS == UINT32_MAX
-                               || uTimeLimitMS == 0)
-                            ? RT_INDEFINITE_WAIT : uTimeLimitMS;
-
-        /* Init buffers. */
-        rc = VBoxServicePipeBufInit(&pData->stdOut, VBOXSERVICECTRLPIPEID_STDOUT,
-                                    false /*fNeedNotificationPipe*/);
+        /* User management. */
+        pThread->pszUser     = RTStrDup(pszUser);
+        pThread->pszPassword = RTStrDup(pszPassword);
+    }
+
+    if (RT_FAILURE(rc)) /* Clean up on failure. */
+        vboxServiceControlExecThreadFree(pThread);
+    return rc;
+}
+
+
+/**
+ * TODO
+ *
+ * @param   pThread
+ */
+void VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread)
+{
+    AssertPtrReturnVoid(pThread);
+
+    VBoxServiceVerbose(3, "ControlExec: [PID %u]: Signalling shutdown ...\n",
+                       pThread->uPID);
+
+    /* First, set the shutdown flag. */
+    ASMAtomicXchgBool(&pThread->fShutdown, true);
+
+    VBOXSERVICECTRLREQUEST ctrlRequest;
+    RT_ZERO(ctrlRequest);
+    ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_QUIT;
+
+    int rc = VBoxServiceControlExecThreadPerform(pThread->uPID, &ctrlRequest);
+    if (RT_FAILURE(rc))
+        VBoxServiceVerbose(3, "ControlExec: [PID %u]: Sending quit request failed with rc=%Rrc\n",
+                           pThread->uPID, rc);
+}
+
+
+static int vboxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread)
+{
+    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
+    int rc = VINF_SUCCESS;
+    if (   pThread->Thread != NIL_RTTHREAD
+        && !pThread->fShutdown) /* Only shutdown threads which aren't yet. */
+    {
+        /* Wait a bit ... */
+        rc = RTThreadWait(pThread->Thread, 30 * 1000 /* Wait 30 seconds max. */, NULL);
+    }
+    return rc;
+}
+
+
+/**
+ * Frees an allocated thread data structure along with all its allocated parameters.
+ *
+ * @param   pThread        Pointer to thread data to free.
+ */
+static void vboxServiceControlExecThreadFree(const PVBOXSERVICECTRLTHREAD pThread)
+{
+    if (pThread)
+    {
+        VBoxServiceVerbose(3, "ControlExec: [PID %u]: Freeing thread data ...\n",
+                           pThread->uPID);
+
+        RTStrFree(pThread->pszCmd);
+        if (pThread->uNumEnvVars)
+        {
+            for (uint32_t i = 0; i < pThread->uNumEnvVars; i++)
+                RTStrFree(pThread->papszEnv[i]);
+            RTMemFree(pThread->papszEnv);
+        }
+        RTGetOptArgvFree(pThread->papszArgs);
+        RTStrFree(pThread->pszUser);
+        RTStrFree(pThread->pszPassword);
+    }
+}
+
+
+/**
+ * Closes the stdin pipe of a guest process.
+ *
+ * @return  IPRT status code.
+ * @param   hPollSet            The polling set.
+ * @param   phStdInW            The standard input pipe handle.
+ */
+static int VBoxServiceControlExecProcCloseStdIn(RTPOLLSET hPollSet, PRTPIPE phStdInW)
+{
+    int rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN);
+    if (rc != VERR_POLL_HANDLE_ID_NOT_FOUND)
+        AssertRC(rc);
+
+    rc = RTPipeClose(*phStdInW);
+    AssertRC(rc);
+    *phStdInW = NIL_RTPIPE;
+
+    return rc;
+}
+
+
+/**
+ * Handle an error event on standard input.
+ *
+ * @return  IPRT status code.
+ * @param   hPollSet            The polling set.
+ * @param   fPollEvt            The event mask returned by RTPollNoResume.
+ * @param   phStdInW            The standard input pipe handle.
+ */
+static int VBoxServiceControlExecProcHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW)
+{
+    NOREF(fPollEvt);
+
+    return VBoxServiceControlExecProcCloseStdIn(hPollSet, phStdInW);
+}
+
+
+/**
+ * Handle pending output data or error on standard out or standard error.
+ *
+ * @returns IPRT status code from client send.
+ * @param   hPollSet            The polling set.
+ * @param   fPollEvt            The event mask returned by RTPollNoResume.
+ * @param   phPipeR             The pipe handle.
+ * @param   idPollHnd           The pipe ID to handle.
+ *
+ */
+static int VBoxServiceControlExecProcHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt,
+                                                       PRTPIPE phPipeR, uint32_t idPollHnd)
+{
+    int rc = VINF_SUCCESS;
+
+#if 0
+    if (fPollEvt & RTPOLL_EVT_READ)
+    {
+        /** @todo Later: Notify the host about the read operation! */
+
+        /* Make sure we go another poll round in case there was too much data
+           for the buffer to hold. */
+        fPollEvt &= RTPOLL_EVT_ERROR;
+
+        /* Remove read event from poll set and just poll for errors from now on.
+         * This is necessary for doing a RTPipeRead. */
+        rc = RTPollSetEventsChange(hPollSet, idPollHnd, RTPOLL_EVT_ERROR);
+        AssertRC(rc);
+    }
+#endif
+
+#ifdef DEBUG_andy
+    VBoxServiceVerbose(4, "ControlExec: HandleOutputEvent fPollEvt=0x%x, idPollHnd=%u\n",
+                       fPollEvt, idPollHnd);
+#endif
+
+    /*
+     * If an error was raised signalled
+     */
+    if (fPollEvt & RTPOLL_EVT_ERROR)
+    {
+        rc = RTPollSetRemove(hPollSet, idPollHnd);
+        AssertRC(rc);
+
+        rc = RTPipeClose(*phPipeR);
+        AssertRC(rc);
+        *phPipeR = NIL_RTPIPE;
+    }
+
+    return rc;
+}
+
+
+int VBoxServiceControlExecProcHandleIPCNotify(RTPOLLSET hPollSet, uint32_t fPollEvt,
+                                              PRTPIPE phNotificationPipeR)
+{
+#ifdef DEBUG_andy
+    VBoxServiceVerbose(4, "ControlExec: HandleIPCNotify\n");
+#endif
+    /* Drain the notification pipe. */
+    uint8_t abBuf[8];
+    size_t cbIgnore;
+    int rc = RTPipeRead(*phNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
+    if (RT_FAILURE(rc))
+        VBoxServiceError("ControlExec: Draining IPC notification pipe failed with rc=%Rrc\n", rc);
+    return rc;
+}
+
+
+static int VBoxServiceControlExecHandleIPCRequest(RTPOLLSET hPollSet, uint32_t fPollEvt,
+                                                  PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR,
+                                                  PVBOXSERVICECTRLREQUEST pRequest)
+{
+#ifdef DEBUG_andy
+    VBoxServiceVerbose(4, "ControlExec: HandleIPCRequest\n");
+#endif
+
+    AssertPtrReturn(phStdInW, VERR_INVALID_POINTER);
+    AssertPtrReturn(phStdOutR, VERR_INVALID_POINTER);
+    AssertPtrReturn(phStdErrR, VERR_INVALID_POINTER);
+    AssertPtrReturn(pRequest, VERR_INVALID_POINTER);
+
+    int rc = VINF_SUCCESS;
+    int rcReq = VINF_SUCCESS; /* Actual request result. */
+
+    switch (pRequest->enmType)
+    {
+        case VBOXSERVICECTRLREQUEST_QUIT: /* Main control asked us to quit. */
+            /** @todo Check for some conditions to check to
+             *        veto quitting. */
+            pRequest->rc = VINF_SUCCESS;
+            break;
+
+        case VBOXSERVICECTRLREQUEST_STDIN_WRITE:
+            /* Fall through is intentional. */
+        case VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF:
+        {
+            AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER);
+            AssertReturn(pRequest->cbData, VERR_INVALID_PARAMETER);
+
+            size_t cbWritten = 0;
+            if (*phStdInW != NIL_RTPIPE)
+            {
+                rcReq = RTPipeWrite(*phStdInW,
+                                    pRequest->pvData, pRequest->cbData, &cbWritten);
+            }
+            else
+                rcReq = VINF_EOF;
+
+            /* If this is the last write we need to close the stdin pipe on our
+             * end and remove it from the poll set. */
+            if (VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF == pRequest->enmType)
+                rc = VBoxServiceControlExecProcCloseStdIn(hPollSet, phStdInW);
+
+            /* Reqport back actual data written (if any). */
+            pRequest->cbData = cbWritten;
+            break;
+        }
+
+        case VBOXSERVICECTRLREQUEST_STDOUT_READ:
+            /* Fall through is intentional. */
+        case VBOXSERVICECTRLREQUEST_STDERR_READ:
+        {
+            AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER);
+            AssertReturn(pRequest->cbData, VERR_INVALID_PARAMETER);
+
+            PRTPIPE pPipeR = pRequest->enmType == VBOXSERVICECTRLREQUEST_STDERR_READ
+                           ? phStdErrR : phStdOutR;
+            AssertPtr(pPipeR);
+
+            size_t cbRead = 0;
+            if (*pPipeR != NIL_RTPIPE)
+            {
+                rcReq = RTPipeRead(*pPipeR,
+                                   pRequest->pvData, pRequest->cbData, &cbRead);
+                if (rcReq == VERR_BROKEN_PIPE)
+                    rcReq = VINF_EOF;
+            }
+            else
+                rcReq = VINF_EOF;
+
+            /* Report back actual data read (if any). */
+            pRequest->cbData = cbRead;
+            break;
+        }
+
+        default:
+            rcReq = VERR_INVALID_PARAMETER;
+            break;
+    }
+
+    pRequest->rc = RT_SUCCESS(rc)
+                 ? rcReq : rc;
+
+    VBoxServiceVerbose(4, "ControlExec: Handled IPC request with rcReq=%Rrc, enmType=%u, cbData=%u\n",
+                       rcReq, pRequest->enmType, pRequest->cbData);
+    return rc;
+}
+
+
+/**
+ * Execution loop which runs in a dedicated per-started-process thread and
+ * handles all pipe input/output and signalling stuff.
+ *
+ * @return  IPRT status code.
+ * @param   pThread                     The process' thread handle.
+ * @param   hProcess                    The actual process handle.
+ * @param   cMsTimeout                  Time limit (in ms) of the process' life time.
+ * @param   hPollSet                    The poll set to use.
+ * @param   hStdInW                     Handle to the process' stdin write end.
+ * @param   hStdOutR                    Handle to the process' stdout read end.
+ * @param   hStdErrR                    Handle to the process' stderr read end.
+ */
+static int VBoxServiceControlExecProcLoop(PVBOXSERVICECTRLTHREAD pThread,
+                                          RTPROCESS hProcess, RTMSINTERVAL cMsTimeout, RTPOLLSET hPollSet,
+                                          PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR)
+{
+    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
+    AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(phStdOutR, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(phStdErrR, VERR_INVALID_PARAMETER);
+
+    int                         rc;
+    int                         rc2;
+    uint64_t const              MsStart             = RTTimeMilliTS();
+    RTPROCSTATUS                ProcessStatus       = { 254, RTPROCEXITREASON_ABEND };
+    bool                        fProcessAlive       = true;
+    bool                        fProcessTimedOut    = false;
+    uint64_t                    MsProcessKilled     = UINT64_MAX;
+    RTMSINTERVAL const          cMsPollBase         = *phStdInW != NIL_RTPIPE
+                                                      ? 100   /* Need to poll for input. */
+                                                      : 1000; /* Need only poll for process exit and aborts. */
+    RTMSINTERVAL                cMsPollCur          = 0;
+
+    /*
+     * Assign PID to thread data.
+     * Also check if there already was a thread with the same PID and shut it down -- otherwise
+     * the first (stale) entry will be found and we get really weird results!
+     */
+    rc = vboxServiceControlExecThreadAssignPID(pThread, hProcess);
+    if (RT_FAILURE(rc))
+    {
+        VBoxServiceError("ControlExec: Unable to assign PID to new thread, rc=%Rrc\n", rc);
+        return rc;
+    }
+
+    /*
+     * Before entering the loop, tell the host that we've started the guest
+     * and that it's now OK to send input to the process.
+     */
+    VBoxServiceVerbose(3, "ControlExec: [PID %u]: Process started, CID=%u, User=%s\n",
+                       pThread->uPID, pThread->uContextID, pThread->pszUser);
+    rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
+                                         pThread->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
+                                         NULL /* pvData */, 0 /* cbData */);
+
+    /*
+     * Process input, output, the test pipe and client requests.
+     */
+    while (   RT_SUCCESS(rc)
+           && RT_UNLIKELY(!pThread->fShutdown))
+    {
+        /*
+         * Wait/Process all pending events.
+         */
+        uint32_t idPollHnd;
+        uint32_t fPollEvt;
+        rc2 = RTPollNoResume(hPollSet, /*cMsPollCur*/ RT_INDEFINITE_WAIT, &fPollEvt, &idPollHnd);
+        if (pThread->fShutdown)
+            continue;
+
+        cMsPollCur = 0; /* No rest until we've checked everything. */
+
+        if (RT_SUCCESS(rc2))
+        {
+            /*VBoxServiceVerbose(4, "ControlExec: [PID %u}: RTPollNoResume idPollHnd=%u\n",
+                                 pThread->uPID, idPollHnd);*/
+            switch (idPollHnd)
+            {
+                case VBOXSERVICECTRLPIPEID_STDIN:
+                    rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW);
+                    break;
+
+                case VBOXSERVICECTRLPIPEID_STDOUT:
+                    rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt,
+                                                                     phStdOutR, idPollHnd);
+                    break;
+
+                case VBOXSERVICECTRLPIPEID_STDERR:
+                    rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt,
+                                                                     phStdErrR, idPollHnd);
+                    break;
+
+                case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
+                    rc = VBoxServiceControlExecProcHandleIPCNotify(hPollSet, fPollEvt,
+                                                                   &pThread->hNotificationPipeR);
+                    /* Fall through is intentional. */
+                default:
+                    /* Handle IPC requests. */
+                    if (RT_SUCCESS(rc))
+                    {
+                        rc = VBoxServiceControlExecHandleIPCRequest(hPollSet, fPollEvt,
+                                                                    phStdInW, phStdOutR, phStdErrR,
+                                                                    pThread->pRequest);
+                    }
+
+                    /* If we were asked to terminate do so ... */
+                    if (pThread->pRequest->enmType == VBOXSERVICECTRLREQUEST_QUIT)
+                        rc = VINF_EOF;
+
+                    /* In any case, regardless of the result, we notify
+                     * the main guest control to unblock it. */
+                    int rc2 = RTSemEventMultiSignal(pThread->RequestEvent);
+                    AssertRC(rc2);
+                    /* No access to pRequest here anymore -- could be out of scope
+                     * or modified already! */
+                    break;
+            }
+            if (RT_FAILURE(rc) || rc == VINF_EOF)
+                break; /* Abort command, or client dead or something. */
+
+            /* Only notification pipe left? Then there's nothing to poll for anymore really.
+             * Bail out .. */
+            if (RTPollSetGetCount(hPollSet) > 1)
+                continue;
+        }
+
+        /*
+         * Check for process death.
+         */
+        if (fProcessAlive)
+        {
+            rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
+            if (RT_SUCCESS_NP(rc2))
+            {
+                fProcessAlive = false;
+                continue;
+            }
+            if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
+                continue;
+            if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
+            {
+                fProcessAlive = false;
+                ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
+                ProcessStatus.iStatus   = 255;
+                AssertFailed();
+            }
+            else
+                AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
+        }
+
+        /*
+         * If the process has terminated, we're should head out.
+         */
+        if (!fProcessAlive)
+            break;
+
+        /*
+         * Check for timed out, killing the process.
+         */
+        uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
+        if (cMsTimeout != RT_INDEFINITE_WAIT)
+        {
+            uint64_t u64Now = RTTimeMilliTS();
+            uint64_t cMsElapsed = u64Now - MsStart;
+            if (cMsElapsed >= cMsTimeout)
+            {
+                VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out (%ums elapsed > %ums timeout), killing ...",
+                                   pThread->uPID, cMsElapsed, cMsTimeout);
+
+                fProcessTimedOut = true;
+                if (    MsProcessKilled == UINT64_MAX
+                    ||  u64Now - MsProcessKilled > 1000)
+                {
+                    if (u64Now - MsProcessKilled > 20*60*1000)
+                        break; /* Give up after 20 mins. */
+                    RTProcTerminate(hProcess);
+                    MsProcessKilled = u64Now;
+                    continue;
+                }
+                cMilliesLeft = 10000;
+            }
+            else
+                cMilliesLeft = cMsTimeout - (uint32_t)cMsElapsed;
+        }
+
+        /* Reset the polling interval since we've done all pending work. */
+        cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
+
+        /*
+         * Need to exit?
+         */
+        if (pThread->fShutdown)
+            break;
+    }
+
+    /*
+     * Try kill the process if it's still alive at this point.
+     */
+    if (fProcessAlive)
+    {
+        if (MsProcessKilled == UINT64_MAX)
+        {
+            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Is still alive and not killed yet\n",
+                               pThread->uPID);
+
+            MsProcessKilled = RTTimeMilliTS();
+            RTProcTerminate(hProcess);
+            RTThreadSleep(500);
+        }
+
+        for (size_t i = 0; i < 10; i++)
+        {
+            VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Waiting to exit ...\n",
+                               pThread->uPID, i + 1);
+            rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
+            if (RT_SUCCESS(rc2))
+            {
+                VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Exited\n",
+                                   pThread->uPID, i + 1);
+                fProcessAlive = false;
+                break;
+            }
+            if (i >= 5)
+            {
+                VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Trying to terminate ...\n",
+                                   pThread->uPID, i + 1);
+                RTProcTerminate(hProcess);
+            }
+            RTThreadSleep(i >= 5 ? 2000 : 500);
+        }
+
+        if (fProcessAlive)
+            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Could not be killed\n", pThread->uPID);
+    }
+
+    /*
+     * If we don't have a client problem (RT_FAILURE(rc)) we'll reply to the
+     * clients exec packet now.
+     */
+    if (RT_SUCCESS(rc))
+    {
+        /* Mark this thread as stopped and do some action required for stopping ... */
+        //VBoxServiceControlExecThreadCleanup(pThread);
+
+        uint32_t uStatus = PROC_STS_UNDEFINED;
+        uint32_t uFlags = 0;
+
+        if (     fProcessTimedOut  && !fProcessAlive && MsProcessKilled != UINT64_MAX)
+        {
+            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out and got killed\n",
+                               pThread->uPID);
+            uStatus = PROC_STS_TOK;
+        }
+        else if (fProcessTimedOut  &&  fProcessAlive && MsProcessKilled != UINT64_MAX)
+        {
+            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out and did *not* get killed\n",
+                               pThread->uPID);
+            uStatus = PROC_STS_TOA;
+        }
+        else if (pThread->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
+        {
+            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Got terminated because system/service is about to shutdown\n",
+                               pThread->uPID);
+            uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
+            uFlags = pThread->uFlags; /* Return handed-in execution flags back to the host. */
+        }
+        else if (fProcessAlive)
+        {
+            VBoxServiceError("ControlExec: [PID %u]: Is alive when it should not!\n",
+                             pThread->uPID);
+        }
+        else if (MsProcessKilled != UINT64_MAX)
+        {
+            VBoxServiceError("ControlExec: [PID %u]: Has been killed when it should not!\n",
+                             pThread->uPID);
+        }
+        else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
+        {
+            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_NORMAL (%u)\n",
+                               pThread->uPID, ProcessStatus.iStatus);
+
+            uStatus = PROC_STS_TEN;
+            uFlags = ProcessStatus.iStatus;
+        }
+        else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
+        {
+            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_SIGNAL (%u)\n",
+                               pThread->uPID, ProcessStatus.iStatus);
+
+            uStatus = PROC_STS_TES;
+            uFlags = ProcessStatus.iStatus;
+        }
+        else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
+        {
+            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_ABEND (%u)\n",
+                               pThread->uPID, ProcessStatus.iStatus);
+
+            uStatus = PROC_STS_TEA;
+            uFlags = ProcessStatus.iStatus;
+        }
+        else
+            VBoxServiceVerbose(1, "ControlExec: [PID %u]: Handling process status %u not implemented\n",
+                               pThread->uPID, ProcessStatus.enmReason);
+
+        rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
+                                             pThread->uPID, uStatus, uFlags,
+                                             NULL /* pvData */, 0 /* cbData */);
+        if (RT_FAILURE(rc))
+            VBoxServiceError("ControlExec: [PID %u]: Error reporting final status to host; rc=%Rrc\n",
+                             pThread->uPID, rc);
+
+        VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended, CID=%u, Status=%u, Flags=%u\n",
+                           pThread->uPID, pThread->uContextID, uStatus, uFlags);
+
+        VBoxServiceVerbose(3, "ControlExec: [PID %u]: Process loop ended with rc=%Rrc\n",
+                           pThread->uPID, rc);
+    }
+    else
+        VBoxServiceError("ControlExec: [PID %u]: Loop failed with rc=%Rrc\n",
+                         pThread->uPID, rc);
+    return rc;
+}
+
+
+/**
+ * Sets up the redirection / pipe / nothing for one of the standard handles.
+ *
+ * @returns IPRT status code.  No client replies made.
+ * @param   fd                  Which standard handle it is (0 == stdin, 1 ==
+ *                              stdout, 2 == stderr).
+ * @param   ph                  The generic handle that @a pph may be set
+ *                              pointing to.  Always set.
+ * @param   pph                 Pointer to the RTProcCreateExec argument.
+ *                              Always set.
+ * @param   phPipe              Where to return the end of the pipe that we
+ *                              should service.  Always set.
+ */
+static int VBoxServiceControlExecSetupPipe(int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
+{
+    AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pph, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
+
+    ph->enmType = RTHANDLETYPE_PIPE;
+    ph->u.hPipe = NIL_RTPIPE;
+    *pph        = NULL;
+    *phPipe     = NIL_RTPIPE;
+
+    int rc;
+
+    /*
+     * Setup a pipe for forwarding to/from the client.
+     * The ph union struct will be filled with a pipe read/write handle
+     * to represent the "other" end to phPipe.
+     */
+    if (fd == 0) /* stdin? */
+    {
+        /* Connect a wrtie pipe specified by phPipe to stdin. */
+        rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
+    }
+    else /* stdout or stderr? */
+    {
+        /* Connect a read pipe specified by phPipe to stdout or stderr. */
+        rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
+    }
+    if (RT_FAILURE(rc))
+        return rc;
+    ph->enmType = RTHANDLETYPE_PIPE;
+    *pph = ph;
+
+    return rc;
+}
+
+
+/**
+ * Expands a file name / path to its real content. This only works on Windows
+ * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting
+ * with system / administrative rights).
+ *
+ * @return  IPRT status code.
+ * @param   pszPath                     Path to resolve.
+ * @param   pszExpanded                 Pointer to string to store the resolved path in.
+ * @param   cbExpanded                  Size (in bytes) of string to store the resolved path.
+ */
+static int VBoxServiceControlExecMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded)
+{
+    int rc = VINF_SUCCESS;
+#ifdef RT_OS_WINDOWS
+    if (!ExpandEnvironmentStrings(pszPath, pszExpanded, cbExpanded))
+        rc = RTErrConvertFromWin32(GetLastError());
+#else
+    /* No expansion for non-Windows yet. */
+    rc = RTStrCopy(pszExpanded, cbExpanded, pszPath);
+#endif
+#ifdef DEBUG
+    VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecMakeFullPath: %s -> %s\n",
+                       pszPath, pszExpanded);
+#endif
+    return rc;
+}
+
+
+/**
+ * Resolves the full path of a specified executable name. This function also
+ * resolves internal VBoxService tools to its appropriate executable path + name.
+ *
+ * @return  IPRT status code.
+ * @param   pszFileName                 File name to resovle.
+ * @param   pszResolved                 Pointer to a string where the resolved file name will be stored.
+ * @param   cbResolved                  Size (in bytes) of resolved file name string.
+ */
+static int VBoxServiceControlExecResolveExecutable(const char *pszFileName, char *pszResolved, size_t cbResolved)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Search the path of our executable. */
+    char szVBoxService[RTPATH_MAX];
+    if (RTProcGetExecutablePath(szVBoxService, sizeof(szVBoxService)))
+    {
+        char *pszExecResolved = NULL;
+        if (   (g_pszProgName && RTStrICmp(pszFileName, g_pszProgName) == 0)
+            || !RTStrICmp(pszFileName, VBOXSERVICE_NAME))
+        {
+            /* We just want to execute VBoxService (no toolbox). */
+            pszExecResolved = RTStrDup(szVBoxService);
+        }
+        else /* Nothing to resolve, copy original. */
+            pszExecResolved = RTStrDup(pszFileName);
+        AssertPtr(pszExecResolved);
+
+        rc = VBoxServiceControlExecMakeFullPath(pszExecResolved, pszResolved, cbResolved);
+#ifdef DEBUG
+        VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecResolveExecutable: %s -> %s\n",
+                           pszFileName, pszResolved);
+#endif
+        RTStrFree(pszExecResolved);
+    }
+    return rc;
+}
+
+
+/**
+ * Constructs the argv command line by resolving environment variables
+ * and relative paths.
+ *
+ * @return IPRT status code.
+ * @param  pszArgv0         First argument (argv0), either original or modified version.
+ * @param  papszArgs        Original argv command line from the host, starting at argv[1].
+ * @param  ppapszArgv       Pointer to a pointer with the new argv command line.
+ *                          Needs to be freed with RTGetOptArgvFree.
+ */
+static int VBoxServiceControlExecPrepareArgv(const char *pszArgv0,
+                                             const char * const *papszArgs, char ***ppapszArgv)
+{
+/** @todo RTGetOptArgvToString converts to MSC quoted string, while
+ *        RTGetOptArgvFromString takes bourne shell according to the docs...
+ * Actually, converting to and from here is a very roundabout way of prepending
+ * an entry (pszFilename) to an array (*ppapszArgv). */
+    int rc = VINF_SUCCESS;
+    char *pszNewArgs = NULL;
+    if (pszArgv0)
+        rc = RTStrAAppend(&pszNewArgs, pszArgv0);
+    if (   RT_SUCCESS(rc)
+        && papszArgs)
+
+    {
+        char *pszArgs;
+        rc = RTGetOptArgvToString(&pszArgs, papszArgs,
+                                  RTGETOPTARGV_CNV_QUOTE_MS_CRT); /* RTGETOPTARGV_CNV_QUOTE_BOURNE_SH */
         if (RT_SUCCESS(rc))
         {
-            rc = VBoxServicePipeBufInit(&pData->stdErr, VBOXSERVICECTRLPIPEID_STDERR,
-                                        false /*fNeedNotificationPipe*/);
+            rc = RTStrAAppend(&pszNewArgs, " ");
             if (RT_SUCCESS(rc))
-                rc = VBoxServicePipeBufInit(&pData->stdIn, VBOXSERVICECTRLPIPEID_STDIN,
-                                            true /*fNeedNotificationPipe*/);
+                rc = RTStrAAppend(&pszNewArgs, pszArgs);
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        int iNumArgsIgnored;
+        rc = RTGetOptArgvFromString(ppapszArgv, &iNumArgsIgnored,
+                                    pszNewArgs ? pszNewArgs : "", NULL /* Use standard separators. */);
+    }
+
+    if (pszNewArgs)
+        RTStrFree(pszNewArgs);
+    return rc;
+}
+
+
+/**
+ * Helper function to create/start a process on the guest.
+ *
+ * @return  IPRT status code.
+ * @param   pszExec                     Full qualified path of process to start (without arguments).
+ * @param   papszArgs                   Pointer to array of command line arguments.
+ * @param   hEnv                        Handle to environment block to use.
+ * @param   fFlags                      Process execution flags.
+ * @param   phStdIn                     Handle for the process' stdin pipe.
+ * @param   phStdOut                    Handle for the process' stdout pipe.
+ * @param   phStdErr                    Handle for the process' stderr pipe.
+ * @param   pszAsUser                   User name (account) to start the process under.
+ * @param   pszPassword                 Password of the specified user.
+ * @param   phProcess                   Pointer which will receive the process handle after
+ *                                      successful process start.
+ */
+static int VBoxServiceControlExecCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
+                                               PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
+                                               const char *pszPassword, PRTPROCESS phProcess)
+{
+    AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER);
+
+    int  rc = VINF_SUCCESS;
+    char szExecExp[RTPATH_MAX];
+#ifdef RT_OS_WINDOWS
+    /*
+     * If sysprep should be executed do this in the context of VBoxService, which
+     * (usually, if started by SCM) has administrator rights. Because of that a UI
+     * won't be shown (doesn't have a desktop).
+     */
+    if (RTStrICmp(pszExec, "sysprep") == 0)
+    {
+        /* Use a predefined sysprep path as default. */
+        char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
+
+        /*
+         * On Windows Vista (and up) sysprep is located in "system32\\sysprep\\sysprep.exe",
+         * so detect the OS and use a different path.
+         */
+        OSVERSIONINFOEX OSInfoEx;
+        RT_ZERO(OSInfoEx);
+        OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+        if (    GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
+            &&  OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
+            &&  OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
+        {
+            rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
+            if (RT_SUCCESS(rc))
+                rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\sysprep\\sysprep.exe");
         }
 
         if (RT_SUCCESS(rc))
         {
-            pThread->enmType = kVBoxServiceCtrlThreadDataExec;
-            pThread->pvData = pData;
-        }
-    }
-
+            char **papszArgsExp;
+            rc = VBoxServiceControlExecPrepareArgv(szSysprepCmd /* argv0 */, papszArgs, &papszArgsExp);
+            if (RT_SUCCESS(rc))
+            {
+                rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */,
+                                    phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
+                                    NULL /* pszPassword */, phProcess);
+            }
+            RTGetOptArgvFree(papszArgsExp);
+        }
+        return rc;
+    }
+#endif /* RT_OS_WINDOWS */
+
+#ifdef VBOXSERVICE_TOOLBOX
+    if (RTStrStr(pszExec, "vbox_") == pszExec)
+    {
+        /* We want to use the internal toolbox (all internal
+         * tools are starting with "vbox_" (e.g. "vbox_cat"). */
+        rc = VBoxServiceControlExecResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp));
+    }
+    else
+    {
+#endif
+        /*
+         * Do the environment variables expansion on executable and arguments.
+         */
+        rc = VBoxServiceControlExecResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
+#ifdef VBOXSERVICE_TOOLBOX
+    }
+#endif
+    if (RT_SUCCESS(rc))
+    {
+        char **papszArgsExp;
+        rc = VBoxServiceControlExecPrepareArgv(pszExec /* Always use the unmodified executable name as argv0. */,
+                                               papszArgs /* Append the rest of the argument vector (if any). */, &papszArgsExp);
+        if (RT_SUCCESS(rc))
+        {
+            uint32_t uProcFlags = 0;
+            if (fFlags)
+            {
+                /* Process Main flag "ExecuteProcessFlag_Hidden". */
+                if (fFlags & RT_BIT(2))
+                    uProcFlags |= RTPROC_FLAGS_HIDDEN;
+                /* Process Main flag "ExecuteProcessFlag_NoProfile". */
+                if (fFlags & RT_BIT(3))
+                    uProcFlags |= RTPROC_FLAGS_NO_PROFILE;
+            }
+
+            /* If no user name specified run with current credentials (e.g.
+             * full service/system rights). This is prohibited via official Main API!
+             *
+             * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication
+             * code (at least on Windows) for running processes as different users
+             * started from our system service. */
+            if (*pszAsUser)
+                uProcFlags |= RTPROC_FLAGS_SERVICE;
+#ifdef DEBUG
+            VBoxServiceVerbose(3, "ControlExec: Command: %s\n", szExecExp);
+            for (size_t i = 0; papszArgsExp[i]; i++)
+                VBoxServiceVerbose(3, "ControlExec:\targv[%ld]: %s\n", i, papszArgsExp[i]);
+#endif
+            /* Do normal execution. */
+            rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags,
+                                phStdIn, phStdOut, phStdErr,
+                                *pszAsUser   ? pszAsUser   : NULL,
+                                *pszPassword ? pszPassword : NULL,
+                                phProcess);
+            RTGetOptArgvFree(papszArgsExp);
+        }
+    }
+    return rc;
+}
+
+/**
+ * The actual worker routine (lopp) for a started guest process.
+ *
+ * @return  IPRT status code.
+ * @param   PVBOXSERVICECTRLTHREAD         Thread data associated with a started process.
+ */
+static DECLCALLBACK(int) VBoxServiceControlExecProcessWorker(PVBOXSERVICECTRLTHREAD pThread)
+{
+    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
+    VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" started\n", pThread->pszCmd);
+
+    int rc = VbglR3GuestCtrlConnect(&pThread->uClientID);
     if (RT_FAILURE(rc))
-        VBoxServiceControlExecThreadDataDestroy(pData);
+    {
+        VBoxServiceError("ControlExec: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
+        RTThreadUserSignal(RTThreadSelf());
+        return rc;
+    }
+
+    bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
+
+    /*
+     * Create the environment.
+     */
+    RTENV hEnv;
+    rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
+    if (RT_SUCCESS(rc))
+    {
+        size_t i;
+        for (i = 0; i < pThread->uNumEnvVars && pThread->papszEnv; i++)
+        {
+            rc = RTEnvPutEx(hEnv, pThread->papszEnv[i]);
+            if (RT_FAILURE(rc))
+                break;
+        }
+        if (RT_SUCCESS(rc))
+        {
+            /*
+             * Setup the redirection of the standard stuff.
+             */
+            /** @todo consider supporting: gcc stuff.c >file 2>&1.  */
+            RTHANDLE    hStdIn;
+            PRTHANDLE   phStdIn;
+            rc = VBoxServiceControlExecSetupPipe(0 /*STDIN_FILENO*/, &hStdIn, &phStdIn, &pThread->pipeStdInW);
+            if (RT_SUCCESS(rc))
+            {
+                RTHANDLE    hStdOut;
+                PRTHANDLE   phStdOut;
+                RTPIPE      hStdOutR;
+                rc = VBoxServiceControlExecSetupPipe(1 /*STDOUT_FILENO*/, &hStdOut, &phStdOut, &hStdOutR);
+                if (RT_SUCCESS(rc))
+                {
+                    RTHANDLE    hStdErr;
+                    PRTHANDLE   phStdErr;
+                    RTPIPE      hStdErrR;
+                    rc = VBoxServiceControlExecSetupPipe(2 /*STDERR_FILENO*/, &hStdErr, &phStdErr, &hStdErrR);
+                    if (RT_SUCCESS(rc))
+                    {
+                        /*
+                         * Create a poll set for the pipes and let the
+                         * transport layer add stuff to it as well.
+                         */
+                        RTPOLLSET hPollSet;
+                        rc = RTPollSetCreate(&hPollSet);
+                        if (RT_SUCCESS(rc))
+                        {
+                            /* Stdin. */
+                            if (RT_SUCCESS(rc))
+                                rc = RTPollSetAddPipe(hPollSet, pThread->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN);
+                            /* Stdout. */
+                            if (RT_SUCCESS(rc))
+                                rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDOUT);
+                            /* Stderr. */
+                            if (RT_SUCCESS(rc))
+                                rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDERR);
+                            /* IPC notification pipe. */
+                            if (RT_SUCCESS(rc))
+                                rc = RTPipeCreate(&pThread->hNotificationPipeR, &pThread->hNotificationPipeW, 0 /* Flags */);
+                            if (RT_SUCCESS(rc))
+                                rc = RTPollSetAddPipe(hPollSet, pThread->hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_IPC_NOTIFY);
+
+                            if (RT_SUCCESS(rc))
+                            {
+                                RTPROCESS hProcess;
+                                rc = VBoxServiceControlExecCreateProcess(pThread->pszCmd, pThread->papszArgs, hEnv, pThread->uFlags,
+                                                                         phStdIn, phStdOut, phStdErr,
+                                                                         pThread->pszUser, pThread->pszPassword,
+                                                                         &hProcess);
+                                if (RT_FAILURE(rc))
+                                    VBoxServiceError("ControlExec: Error starting process, rc=%Rrc\n", rc);
+                                /*
+                                 * Tell the control thread that it can continue
+                                 * spawning services. This needs to be done after the new
+                                 * process has been started because otherwise signal handling
+                                 * on (Open) Solaris does not work correctly (see #5068).
+                                 */
+                                int rc2 = RTThreadUserSignal(RTThreadSelf());
+                                if (RT_FAILURE(rc2))
+                                    rc = rc2;
+                                fSignalled = true;
+
+                                if (RT_SUCCESS(rc))
+                                {
+                                    /*
+                                     * Close the child ends of any pipes and redirected files.
+                                     */
+                                    rc2 = RTHandleClose(phStdIn);   AssertRC(rc2);
+                                    phStdIn    = NULL;
+                                    rc2 = RTHandleClose(phStdOut);  AssertRC(rc2);
+                                    phStdOut   = NULL;
+                                    rc2 = RTHandleClose(phStdErr);  AssertRC(rc2);
+                                    phStdErr   = NULL;
+
+                                    /* Enter the process loop. */
+                                    rc = VBoxServiceControlExecProcLoop(pThread,
+                                                                        hProcess, pThread->uTimeLimitMS, hPollSet,
+                                                                        &pThread->pipeStdInW, &hStdOutR, &hStdErrR);
+
+                                    /* Before cleaning up everything else, remove the thread from our thread list. */
+                                    VBoxServiceControlRemoveThread(pThread);
+
+                                    /*
+                                     * The handles that are no longer in the set have
+                                     * been closed by the above call in order to prevent
+                                     * the guest from getting stuck accessing them.
+                                     * So, NIL the handles to avoid closing them again.
+                                     */
+                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_IPC_NOTIFY, NULL)))
+                                    {
+                                        pThread->hNotificationPipeR = NIL_RTPIPE;
+                                        pThread->hNotificationPipeW = NIL_RTPIPE;
+                                    }
+                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDERR, NULL)))
+                                        hStdErrR = NIL_RTPIPE;
+                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDOUT, NULL)))
+                                        hStdOutR = NIL_RTPIPE;
+                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN, NULL)))
+                                        pThread->pipeStdInW = NIL_RTPIPE;
+                                }
+                                else /* Something went wrong; report error! */
+                                {
+                                    VBoxServiceError("ControlExec: Could not start process '%s' (CID: %u)! Error: %Rrc\n",
+                                                     pThread->pszCmd, pThread->uContextID, rc);
+
+                                    rc2 = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, pThread->uPID,
+                                                                          PROC_STS_ERROR, rc,
+                                                                          NULL /* pvData */, 0 /* cbData */);
+                                    if (RT_FAILURE(rc2))
+                                        VBoxServiceError("ControlExec: Could not report process start error! Error: %Rrc (process error %Rrc)\n",
+                                                         rc2, rc);
+                                }
+                            }
+                            RTPollSetDestroy(hPollSet);
+
+                            RTPipeClose(pThread->hNotificationPipeR);
+                            pThread->hNotificationPipeR = NIL_RTPIPE;
+                            RTPipeClose(pThread->hNotificationPipeW);
+                            pThread->hNotificationPipeW = NIL_RTPIPE;
+                        }
+                        RTPipeClose(hStdErrR);
+                        hStdErrR = NIL_RTPIPE;
+                        RTHandleClose(phStdErr);
+                    }
+                    RTPipeClose(hStdOutR);
+                    hStdOutR = NIL_RTPIPE;
+                    RTHandleClose(phStdOut);
+                }
+                RTPipeClose(pThread->pipeStdInW);
+                RTHandleClose(phStdIn);
+            }
+        }
+        RTEnvDestroy(hEnv);
+    }
+
+    VBoxServiceVerbose(3, "ControlExec: [PID %u]: Thread of process \"%s\" ended with rc=%Rrc\n",
+                       pThread->uPID, pThread->pszCmd, rc);
+
+    /* Disconnect from guest control service. */
+    VbglR3GuestCtrlDisconnect(pThread->uClientID);
+    pThread->uClientID = 0;
+
+    ASMAtomicXchgBool(&pThread->fStarted, false);
+    ASMAtomicXchgBool(&pThread->fStopped, true);
+
+    int rc2 = VBoxServiceControlExecThreadShutdown(pThread);
+    if (RT_SUCCESS(rc2))
+        VBoxServiceControlExecThreadDestroy(pThread);
+
+    /*
+     * If something went wrong signal the user event so that others don't wait
+     * forever on this thread.
+     */
+    if (RT_FAILURE(rc) && !fSignalled)
+        RTThreadUserSignal(RTThreadSelf());
+
+    return rc;
+}
+
+
+/**
+ * Thread main routine for a started process.
+ *
+ * @return IPRT status code.
+ * @param  RTTHREAD             Pointer to the thread's data.
+ * @param  void*                User-supplied argument pointer.
+ *
+ */
+static DECLCALLBACK(int) VBoxServiceControlExecThread(RTTHREAD ThreadSelf, void *pvUser)
+{
+    PVBOXSERVICECTRLTHREAD pThread = (VBOXSERVICECTRLTHREAD*)pvUser;
+    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
+    return VBoxServiceControlExecProcessWorker(pThread);
+}
+
+
+/**
+ * Executes (starts) a process on the guest. This causes a new thread to be created
+ * so that this function will not block the overall program execution.
+ *
+ * @return  IPRT status code.
+ * @param   uClientID                   Client ID for accessing host service.
+ * @param   uContextID                  Context ID to associate the process to start with.
+ * @param   pszCmd                      Full qualified path of process to start (without arguments).
+ * @param   uFlags                      Process execution flags.
+ * @param   pszArgs                     String of arguments to pass to the process to start.
+ * @param   uNumArgs                    Number of arguments specified in pszArgs.
+ * @param   pszEnv                      String of environment variables ("FOO=BAR") to pass to the process
+ *                                      to start.
+ * @param   cbEnv                       Size (in bytes) of environment variables.
+ * @param   uNumEnvVars                 Number of environment variables specified in pszEnv.
+ * @param   pszUser                     User name (account) to start the process under.
+ * @param   pszPassword                 Password of specified user name (account).
+ * @param   uTimeLimitMS                Time limit (in ms) of the process' life time.
+ * @param   ppNode                       The thread's list node to insert into the global thread list
+ *                                      on success.
+ */
+int VBoxServiceControlThreadStart(uint32_t uClientID, uint32_t uContextID,
+                                  const char *pszCmd, uint32_t uFlags,
+                                  const char *pszArgs, uint32_t uNumArgs,
+                                  const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
+                                  const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS,
+                                  PRTLISTNODE *ppNode)
+{
+    AssertPtrReturn(ppNode, VERR_INVALID_POINTER);
+
+    /*
+     * Allocate new thread data and assign it to our thread list.
+     */
+    PVBOXSERVICECTRLTHREAD pThread = (PVBOXSERVICECTRLTHREAD)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREAD));
+    if (!pThread)
+        return VERR_NO_MEMORY;
+
+    int rc = VBoxServiceControlExecThreadAlloc(pThread,
+                                               uContextID,
+                                               pszCmd, uFlags,
+                                               pszArgs, uNumArgs,
+                                               pszEnv, cbEnv, uNumEnvVars,
+                                               pszUser, pszPassword,
+                                               uTimeLimitMS);
+    if (RT_SUCCESS(rc))
+    {
+        static uint32_t uCtrlExecThread = 0;
+        char szThreadName[32];
+        if (!RTStrPrintf(szThreadName, sizeof(szThreadName), "controlexec%ld", uCtrlExecThread++))
+            AssertMsgFailed(("Unable to create unique control exec thread name!\n"));
+
+        rc = RTThreadCreate(&pThread->Thread, VBoxServiceControlExecThread,
+                            (void *)(PVBOXSERVICECTRLTHREAD*)pThread, 0,
+                            RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, szThreadName);
+        if (RT_FAILURE(rc))
+        {
+            VBoxServiceError("ControlExec: RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n",
+                             rc, pThread);
+        }
+        else
+        {
+            VBoxServiceVerbose(4, "ControlExec: Waiting for thread to initialize ...\n");
+
+            /* Wait for the thread to initialize. */
+            RTThreadUserWait(pThread->Thread, 60 * 1000 /* 60 seconds max. */);
+            if (pThread->fShutdown)
+            {
+                VBoxServiceError("ControlExec: Thread for process \"%s\" failed to start!\n", pszCmd);
+                rc = VERR_GENERAL_FAILURE;
+            }
+            else
+            {
+                pThread->fStarted = true;
+
+                /* Return the thread's node. */
+                *ppNode = &pThread->Node;
+            }
+        }
+    }
+
+    if (RT_FAILURE(rc))
+        RTMemFree(pThread);
+
     return rc;
 }
@@ -169,433 +1343,167 @@
  * @param   uPID           PID to assign to the specified guest control execution thread.
  */
-int VBoxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREADDATAEXEC pData, uint32_t uPID)
-{
-    AssertPtrReturn(pData, VERR_INVALID_POINTER);
+int vboxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID)
+{
+    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
     AssertReturn(uPID, VERR_INVALID_PARAMETER);
 
-    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
-    if (RT_SUCCESS(rc))
-    {
-        /* Search an old thread using the desired PID and shut it down completely -- it's
-         * not used anymore. */
-        PVBOXSERVICECTRLTHREAD pOldNode = vboxServiceControlExecThreadGetByPID(uPID);
-        if (   pOldNode
-            && pOldNode->pvData != pData)
-        {
-            PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pOldNode->Node, VBOXSERVICECTRLTHREAD, Node);
-
-            VBoxServiceVerbose(3, "ControlExec: PID %u was used before, shutting down stale exec thread ...\n",
-                               uPID);
-            AssertPtr(pOldNode->pvData);
-            rc = VBoxServiceControlExecThreadShutdown(pOldNode);
-            if (RT_FAILURE(rc))
+    int rc = VINF_SUCCESS;
+
+    /* Search an old thread using the desired PID and shut it down completely -- it's
+     * not used anymore. */
+    PVBOXSERVICECTRLTHREAD pOldThread = VBoxServiceControlGetThreadByPID(uPID);
+    if (   pOldThread
+        && pOldThread != pThread)
+    {
+        PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pOldThread->Node, VBOXSERVICECTRLTHREAD, Node);
+
+        VBoxServiceVerbose(3, "ControlExec: PID %u was used before, shutting down stale exec thread ...\n",
+                           uPID);
+        rc = VBoxServiceControlExecThreadShutdown(pOldThread);
+        if (RT_FAILURE(rc))
+        {
+            VBoxServiceVerbose(3, "ControlExec: Unable to shut down stale exec thread, rc=%Rrc\n", rc);
+            /* Keep going. */
+        }
+    }
+
+    /* Assign PID to current thread. */
+    pThread->uPID = uPID;
+
+    return rc;
+}
+
+
+/**
+ * TODO
+ *
+ * @return  IPRT status code.
+ * @return  int
+ * @param   uPID
+ * @param   pRequest
+ */
+int VBoxServiceControlExecThreadPerform(uint32_t uPID, PVBOXSERVICECTRLREQUEST pRequest)
+{
+    AssertPtrReturn(pRequest, VERR_INVALID_POINTER);
+    AssertReturn(pRequest->enmType > VBOXSERVICECTRLREQUEST_UNKNOWN, VERR_INVALID_PARAMETER);
+    /* Rest in pRequest is optional (based on the request type). */
+
+    VBoxServiceVerbose(4, "ControlExec: [PID %u]: Performing enmType=%u, pvData=0x%p, cbData=%u ...\n",
+                       uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData);
+
+    int rc;
+    const PVBOXSERVICECTRLTHREAD pThread = VBoxServiceControlGetThreadByPID(uPID);
+    if (pThread)
+    {
+        rc = RTCritSectEnter(&pThread->CritSect);
+        if (RT_SUCCESS(rc))
+        {
+            /* Set request structure pointer. */
+            pThread->pRequest = pRequest;
+
+            /** @todo To speed up simultaneous guest process handling we could add a worker threads in order
+             *        to wait for the request to happen. Later. */
+
+            /* Wake up guest thrad by sending a wakeup byte to the notification pipe so
+             * that RTPoll unblocks (returns) and we then can do our requested operation. */
+            if (pThread->hNotificationPipeW == NIL_RTPIPE)
+                rc = VERR_BROKEN_PIPE;
+            size_t cbWritten;
+            if (RT_SUCCESS(rc))
+                rc = RTPipeWrite(pThread->hNotificationPipeW, "i", 1, &cbWritten);
+            if (RT_SUCCESS(rc) && cbWritten)
             {
-                VBoxServiceVerbose(3, "ControlExec: Unable to shut down stale exec thread, rc=%Rrc\n", rc);
-                /* Keep going. */
-            }
-
-            RTListNodeRemove(&pOldNode->Node);
-            RTMemFree(pOldNode);
-        }
-
-        /* Assign PID to current thread. */
-        pData->uPID = uPID;
-        VBoxServicePipeBufSetPID(&pData->stdIn, pData->uPID);
-        VBoxServicePipeBufSetPID(&pData->stdOut, pData->uPID);
-        VBoxServicePipeBufSetPID(&pData->stdErr, pData->uPID);
-
-        int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-
-    return rc;
-}
-
-
-/**
- *  Frees an allocated thread data structure along with all its allocated parameters.
- *
- * @param   pData          Pointer to thread data to free.
- */
-void VBoxServiceControlExecThreadDataDestroy(PVBOXSERVICECTRLTHREADDATAEXEC pData)
-{
-    if (pData)
-    {
-        VBoxServiceVerbose(3, "ControlExec: [PID %u]: Destroying thread data ...\n",
-                           pData->uPID);
-
-        RTStrFree(pData->pszCmd);
-        if (pData->uNumEnvVars)
-        {
-            for (uint32_t i = 0; i < pData->uNumEnvVars; i++)
-                RTStrFree(pData->papszEnv[i]);
-            RTMemFree(pData->papszEnv);
-        }
-        RTGetOptArgvFree(pData->papszArgs);
-        RTStrFree(pData->pszUser);
-        RTStrFree(pData->pszPassword);
-
-        VBoxServicePipeBufDestroy(&pData->stdOut);
-        VBoxServicePipeBufDestroy(&pData->stdErr);
-        VBoxServicePipeBufDestroy(&pData->stdIn);
-
-        RTMemFree(pData);
-        pData = NULL;
-    }
-}
-
-
-/**
- * Finds a (formerly) started process given by its PID.
- * Internal function, does not do locking -- this must be done from the caller function!
- *
- * @return  PVBOXSERVICECTRLTHREAD      Process structure if found, otherwise NULL.
- * @param   uPID                        PID to search for.
- */
-PVBOXSERVICECTRLTHREAD vboxServiceControlExecThreadGetByPID(uint32_t uPID)
-{
-    PVBOXSERVICECTRLTHREAD pNode = NULL;
-    RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
-    {
-        if (pNode->enmType == kVBoxServiceCtrlThreadDataExec)
-        {
-            PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
-            if (pData && pData->uPID == uPID)
-                return pNode;
-        }
-    }
-    return NULL;
-}
-
-
-/**
- * Injects input to a specified running process.
- *
- * @return  IPRT status code.
- * @param   uPID                    PID of process to set the input for.
- * @param   fPendingClose           Flag indicating whether this is the last input block sent to the process.
- * @param   pBuf                    Pointer to a buffer containing the actual input data.
- * @param   cbSize                  Size (in bytes) of the input buffer data.
- * @param   pcbWritten              Pointer to number of bytes written to the process.  Optional.
- */
-int VBoxServiceControlExecThreadSetInput(uint32_t uPID, bool fPendingClose, uint8_t *pBuf,
-                                         uint32_t cbSize, uint32_t *pcbWritten)
-{
-    AssertPtrReturn(pBuf, VERR_INVALID_PARAMETER);
-
-    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
-    if (RT_SUCCESS(rc))
-    {
-        PVBOXSERVICECTRLTHREAD pNode = vboxServiceControlExecThreadGetByPID(uPID);
-        if (pNode)
-        {
-            PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
-            AssertPtr(pData);
-
-            if (VBoxServicePipeBufIsEnabled(&pData->stdIn))
-            {
-                /*
-                 * Feed the data to the pipe.
-                 */
-                uint32_t cbWritten;
-                rc = VBoxServicePipeBufWriteToBuf(&pData->stdIn, pBuf,
-                                                  cbSize, fPendingClose, &cbWritten);
-                if (pcbWritten)
-                    *pcbWritten = cbWritten;
-            }
-            else
-            {
-                /* If input buffer is not enabled anymore we cannot handle that data ... */
-                rc = VERR_BAD_PIPE;
-            }
-        }
-        else
-            rc = VERR_NOT_FOUND; /* PID not found! */
-        RTCritSectLeave(&g_GuestControlThreadsCritSect);
-    }
-    return rc;
-}
-
-
-/**
- * Gets output from stdout/stderr of a specified process.
- *
- * @return  IPRT status code.
- * @param   uPID                    PID of process to retrieve the output from.
- * @param   uHandleId               Stream ID (stdout = 0, stderr = 2) to get the output from.
- * @param   uTimeout                Timeout (in ms) to wait for output becoming available.
- * @param   pBuf                    Pointer to a pre-allocated buffer to store the output.
- * @param   cbSize                  Size (in bytes) of the pre-allocated buffer.
- * @param   pcbRead                 Pointer to number of bytes read.  Optional.
- */
-int VBoxServiceControlExecThreadGetOutput(uint32_t uPID, uint32_t uHandleId, uint32_t uTimeout,
-                                          uint8_t *pBuf, uint32_t cbSize, uint32_t *pcbRead)
-{
-    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
-    AssertReturn(cbSize, VERR_INVALID_PARAMETER);
-
-    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
-    if (RT_SUCCESS(rc))
-    {
-        const PVBOXSERVICECTRLTHREAD pThread = vboxServiceControlExecThreadGetByPID(uPID);
-        if (pThread)
-        {
-            const PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
-            AssertPtr(pData);
-
-            PVBOXSERVICECTRLEXECPIPEBUF pPipeBuf = NULL;
-            switch (uHandleId)
-            {
-                case OUTPUT_HANDLE_ID_STDERR:
-                    pPipeBuf = &pData->stdErr;
-                    break;
-
-                case OUTPUT_HANDLE_ID_STDOUT:
-                case OUTPUT_HANDLE_ID_STDOUT_DEPRECATED:
-                    pPipeBuf = &pData->stdOut;
-                    break;
-
-                default:
-                    rc = VERR_NOT_FOUND; /* Handle ID not found! */
-                    break;
-            }
-
-            if (RT_SUCCESS(rc))
-            {
-                AssertPtr(pPipeBuf);
-
-    #ifdef DEBUG_andy
-                VBoxServiceVerbose(4, "ControlExec: [PID %u]: Getting output from pipe buffer %u ...\n",
-                                   uPID, pPipeBuf->uPipeId);
-    #endif
-                /* If the stdout pipe buffer is enabled (that is, still could be filled by a running
-                 * process) wait for the signal to arrive so that we don't return without any actual
-                 * data read. */
-                bool fEnabled = VBoxServicePipeBufIsEnabled(pPipeBuf);
-                if (fEnabled)
-                {
-    #ifdef DEBUG_andy
-                    VBoxServiceVerbose(4, "ControlExec: [PID %u]: Waiting for pipe buffer %u (%ums)\n",
-                                       uPID, pPipeBuf->uPipeId, uTimeout);
-    #endif
-                    rc = VBoxServicePipeBufWaitForEvent(pPipeBuf, uTimeout);
-                }
+                /* Wait on the request to get completed (or we are asked to abort/shutdown). */
+                rc = RTSemEventMultiWait(pThread->RequestEvent, RT_INDEFINITE_WAIT);
                 if (RT_SUCCESS(rc))
                 {
-                    uint32_t cbRead = cbSize; /* Read as much as possible. */
-                    rc = VBoxServicePipeBufRead(pPipeBuf, pBuf, cbSize, &cbRead);
-                    if (RT_SUCCESS(rc))
-                    {
-                        if (   !cbRead
-                            && fEnabled)
-                        {
-                            AssertReleaseMsg(!VBoxServicePipeBufIsEnabled(pPipeBuf),
-                                             ("[PID %u]: Waited (%ums) for active pipe buffer %u (%u size, %u bytes left), but nothing read!\n",
-                                             uPID, uTimeout, pPipeBuf->uPipeId, pPipeBuf->cbSize, pPipeBuf->cbSize - pPipeBuf->cbOffset));
-                        }
-                        if (pcbRead)
-                            *pcbRead = cbRead;
-                    }
-                    else
-                        VBoxServiceError("ControlExec: [PID %u]: Unable to read from pipe buffer %u, rc=%Rrc\n",
-                                         uPID, pPipeBuf->uPipeId, rc);
+                    VBoxServiceVerbose(4, "ControlExec: [PID %u]: Performed with rc=%Rrc, cbData=%u\n",
+                                       uPID, pRequest->rc, pRequest->cbData);
+
+                    /* Give back overall request result. */
+                    rc = pRequest->rc;
+
+                    /* Reset the semaphore. */
+                    int rc2 = RTSemEventMultiReset(pThread->RequestEvent);
+                    if (RT_FAILURE(rc2))
+                        VBoxServiceError("ControlExec: Unable to reset request event, rc=%Rrc\n", rc2);
                 }
             }
-        }
-        else
-            rc = VERR_NOT_FOUND; /* PID not found! */
-
-        int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
+
+            int rc2 = RTCritSectLeave(&pThread->CritSect);
+            AssertRCReturn(rc2, rc2);
+        }
+    }
+    else /* PID not found! */
+        rc = VERR_NOT_FOUND;
+
+    VBoxServiceVerbose(4, "ControlExec: [PID %u]: Performed enmType=%u, pvData=0x%p, cbData=%u with rc=%Rrc\n",
+                       uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData, rc);
+    return rc;
+}
+
+
+/**
+ * Removes the guest thread from the global guest thread list and finally
+ * destroys it. Does not do locking, must be done by the caller!
+ *
+ * @param   pThread                 Thread to destroy.
+ */
+void VBoxServiceControlExecThreadDestroy(PVBOXSERVICECTRLTHREAD pThread)
+{
+    if (!pThread)
+        return;
+
+    if (RTCritSectIsInitialized(&pThread->CritSect))
+        RTCritSectDelete(&pThread->CritSect);
+
+    /* Destroy thread structure as final step. */
+    RTMemFree(pThread);
+    pThread = NULL;
+}
+
+
+/**
+ * Shuts down a guest thread.
+ * Does not do locking, must be done by the caller!
+ *
+ * @return  IPRT status code.
+ * @param   pThread                 Thread to shut down.
+ */
+int VBoxServiceControlExecThreadShutdown(PVBOXSERVICECTRLTHREAD pThread)
+{
+    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
+
+    if (ASMAtomicReadBool(&pThread->fShutdown)) /* Already shut down. */
+        return VINF_SUCCESS;
+
+    /* First, signal shut down. */
+    VBoxServiceControlThreadSignalShutdown(pThread);
+
+    /*
+     * Wait for thread to shutdown.
+     */
+    VBoxServiceVerbose(2, "ControlExec: [PID %u]: Waitng for shutting down ...\n",
+                       pThread->uPID);
+    int rc;
+    for (int i = 0; i < 3; i++)
+    {
+        rc = vboxServiceControlThreadWaitForShutdown(pThread);
         if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-    return rc;
-}
-
-
-int VBoxServiceControlExecThreadRemove(uint32_t uPID)
-{
-    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
-    if (RT_SUCCESS(rc))
-    {
-        PVBOXSERVICECTRLTHREAD pThread = vboxServiceControlExecThreadGetByPID(uPID);
-        if (pThread)
-        {
-            Assert(pThread->fStarted != pThread->fStopped);
-            if (pThread->fStopped) /* Only shut down stopped threads. */
-            {
-                VBoxServiceVerbose(4, "ControlExec: [PID %u]: Removing thread ... \n",
-                                   uPID);
-
-                rc = VBoxServiceControlExecThreadShutdown(pThread);
-
-                RTListNodeRemove(&pThread->Node);
-                RTMemFree(pThread);
-            }
-        }
-        else
-            rc = VERR_NOT_FOUND;
-
-        int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-
-    return rc;
-}
-
-/* Does not do locking, must be done by the caller! */
-int VBoxServiceControlExecThreadShutdown(const PVBOXSERVICECTRLTHREAD pThread)
-{
-    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
-
-    if (pThread->enmType == kVBoxServiceCtrlThreadDataExec)
-    {
-        PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
-        if (!pData) /* Already destroyed execution data. */
-            return VINF_SUCCESS;
-        if (pThread->fStarted)
-        {
-            VBoxServiceVerbose(2, "ControlExec: [PID %u]: Shutting down a still running thread without stopping is not possible!\n",
-                               pData->uPID);
-            return VERR_INVALID_PARAMETER;
-        }
-
-        VBoxServiceVerbose(2, "ControlExec: [PID %u]: Shutting down, will not be served anymore\n",
-                           pData->uPID);
-        VBoxServiceControlExecThreadDataDestroy(pData);
-    }
-
-    VBoxServiceControlThreadSignalShutdown(pThread);
-    return VBoxServiceControlThreadWaitForShutdown(pThread);
-}
-
-
-int VBoxServiceControlExecThreadStartAllowed(bool *pbAllowed)
-{
-    AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER);
-
-    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
-    if (RT_SUCCESS(rc))
-    {
-        /*
-         * Check if we're respecting our memory policy by checking
-         * how many guest processes are started and served already.
-         */
-        bool fLimitReached = false;
-        if (g_GuestControlProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
-        {
-            /** @todo Put running/stopped (+ memory alloc) stats into global struct! */
-            uint32_t uProcsRunning = 0;
-            uint32_t uProcsStopped = 0;
-            PVBOXSERVICECTRLTHREAD pNode;
-            RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
-            {
-                if (pNode->enmType == kVBoxServiceCtrlThreadDataExec)
-                {
-                    Assert(pNode->fStarted != pNode->fStopped);
-                    if (pNode->fStarted)
-                        uProcsRunning++;
-                    else if (pNode->fStopped)
-                        uProcsStopped++;
-                    else
-                        AssertMsgFailed(("Process neither started nor stopped!?\n"));
-                }
-            }
-
-            VBoxServiceVerbose(2, "ControlExec: Maximum served guest processes set to %u, running=%u, stopped=%u\n",
-                               g_GuestControlProcsMaxKept, uProcsRunning, uProcsStopped);
-
-            int32_t iProcsLeft = (g_GuestControlProcsMaxKept - uProcsRunning - 1);
-            if (iProcsLeft < 0)
-            {
-                VBoxServiceVerbose(3, "ControlExec: Maximum running guest processes reached (%u)\n",
-                                   g_GuestControlProcsMaxKept);
-                fLimitReached = true;
-            }
-            else if (uProcsStopped > (uint32_t)iProcsLeft)
-            {
-                uint32_t uProcsToKill = uProcsStopped - iProcsLeft;
-                Assert(uProcsToKill);
-                VBoxServiceVerbose(3, "ControlExec: Shutting down %ld stopped guest processes\n", uProcsToKill);
-
-                RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
-                {
-                    if (   pNode->enmType == kVBoxServiceCtrlThreadDataExec
-                        && pNode->fStopped)
-                    {
-                        PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pNode->Node, VBOXSERVICECTRLTHREAD, Node);
-
-                        int rc2 = VBoxServiceControlExecThreadShutdown(pNode);
-                        if (RT_FAILURE(rc2))
-                        {
-                            VBoxServiceError("ControlExec: Unable to shut down thread due to policy, rc=%Rrc\n", rc2);
-                            if (RT_SUCCESS(rc))
-                                rc = rc2;
-                            /* Keep going. */
-                        }
-
-                        RTListNodeRemove(&pNode->Node);
-                        RTMemFree(pNode);
-                        pNode = pNext;
-
-                        Assert(uProcsToKill);
-                        uProcsToKill--;
-                        if (!uProcsToKill)
-                            break;
-                    }
-                }
-                Assert(uProcsToKill == 0);
-            }
-        }
-
-        *pbAllowed = !fLimitReached;
-
-        int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-
-    return rc;
-}
-
-
-/**
- * Marks an guest execution thread as stopped and cleans up its internal pipe buffers.
- *
- * @param   pThread                 Pointer to guest execution thread.
- */
-void VBoxServiceControlExecThreadStop(const PVBOXSERVICECTRLTHREAD pThread)
-{
-    AssertPtr(pThread);
-
-    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
-    if (RT_SUCCESS(rc))
-    {
-        if (pThread->fStarted)
-        {
-            const PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
-            if (pData)
-            {
-                VBoxServiceVerbose(3, "ControlExec: [PID %u]: Marking as stopped\n", pData->uPID);
-
-                VBoxServicePipeBufSetStatus(&pData->stdIn, false /* Disabled */);
-                VBoxServicePipeBufSetStatus(&pData->stdOut, false /* Disabled */);
-                VBoxServicePipeBufSetStatus(&pData->stdErr, false /* Disabled */);
-
-                /* Since the process is not alive anymore, destroy its local
-                 * stdin pipe buffer - it's not used anymore and can eat up quite
-                 * a bit of memory. */
-                VBoxServicePipeBufDestroy(&pData->stdIn);
-            }
-
-            /* Mark as stopped. */
-            ASMAtomicXchgBool(&pThread->fStarted, false);
-            ASMAtomicXchgBool(&pThread->fStopped, true);
-        }
-
-        RTCritSectLeave(&g_GuestControlThreadsCritSect);
-    }
-}
-
+            break;
+
+        VBoxServiceError("ControlExec: [PID %u]: Attempt #%d: Waiting for shutdown failed with rc=%Rrc ...\n",
+                         pThread->uPID, i + 1, rc);
+        RTThreadSleep(500); /* Wait a bit before next try. */
+    }
+
+    /* Do not destroy critical section here! */
+
+    /*
+     * Destroy thread-specific data.
+     */
+    vboxServiceControlExecThreadFree(pThread);
+
+    return rc;
+}
+
Index: trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.h
===================================================================
--- trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.h	(revision 39278)
+++ trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.h	(revision 39279)
@@ -21,19 +21,8 @@
 #include "VBoxServiceInternal.h"
 
-int VBoxServiceControlExecThreadAlloc(PVBOXSERVICECTRLTHREAD pThread,
-                                      uint32_t u32ContextID,
-                                      const char *pszCmd, uint32_t uFlags,
-                                      const char *pszArgs, uint32_t uNumArgs,
-                                      const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
-                                      const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS);
-int VBoxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREADDATAEXEC pData, uint32_t uPID);
-void VBoxServiceControlExecThreadDataDestroy(PVBOXSERVICECTRLTHREADDATAEXEC pData);
-int VBoxServiceControlExecThreadGetOutput(uint32_t uPID, uint32_t uHandleId, uint32_t uTimeout,
-                                          uint8_t *pBuf, uint32_t cbSize, uint32_t *pcbRead);
-int VBoxServiceControlExecThreadRemove(uint32_t uPID);
-int VBoxServiceControlExecThreadSetInput(uint32_t uPID, bool fPendingClose, uint8_t *pBuf,
-                                         uint32_t cbSize, uint32_t *pcbWritten);
-int VBoxServiceControlExecThreadStartAllowed(bool *pbAllowed);
-void VBoxServiceControlExecThreadStop(const PVBOXSERVICECTRLTHREAD pThread);
+void VBoxServiceControlExecThreadDestroy(PVBOXSERVICECTRLTHREAD pThread);
+int VBoxServiceControlExecThreadPerform(uint32_t uPID, PVBOXSERVICECTRLREQUEST pRequest);
+int VBoxServiceControlExecThreadShutdown(const PVBOXSERVICECTRLTHREAD pThread);
+
 #endif  /* !___VBoxServiceControlExecThread_h */
 
Index: trunk/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h
===================================================================
--- trunk/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h	(revision 39278)
+++ trunk/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h	(revision 39279)
@@ -108,86 +108,72 @@
 
 #ifdef VBOX_WITH_GUEST_CONTROL
-typedef enum VBOXSERVICECTRLTHREADDATATYPE
-{
-    kVBoxServiceCtrlThreadDataUnknown = 0,
-    kVBoxServiceCtrlThreadDataExec
-} VBOXSERVICECTRLTHREADDATATYPE;
-
+/**
+ * Pipe IDs for handling the guest process poll set.
+ */
 typedef enum VBOXSERVICECTRLPIPEID
 {
-    VBOXSERVICECTRLPIPEID_STDIN = 0,
-    VBOXSERVICECTRLPIPEID_STDIN_ERROR,
-    VBOXSERVICECTRLPIPEID_STDIN_WRITABLE,
-    VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY,
-    VBOXSERVICECTRLPIPEID_STDOUT,
-    VBOXSERVICECTRLPIPEID_STDERR
+    VBOXSERVICECTRLPIPEID_UNKNOWN           = 0,
+    VBOXSERVICECTRLPIPEID_STDIN             = 10,
+    VBOXSERVICECTRLPIPEID_STDIN_WRITABLE    = 11,
+    /** Pipe for reading from guest process' stdout. */
+    VBOXSERVICECTRLPIPEID_STDOUT            = 40,
+    /** Pipe for reading from guest process' stderr. */
+    VBOXSERVICECTRLPIPEID_STDERR            = 50,
+    /** Notification pipe for waking up the guest process
+     *  control thread. */
+    VBOXSERVICECTRLPIPEID_IPC_NOTIFY        = 100
 } VBOXSERVICECTRLPIPEID;
 
 /**
- * Structure for holding buffered pipe data.
- */
-typedef struct
-{
-    /** The PID the pipe is assigned to. */
-    uint32_t    uPID;
-    /** The pipe's Id of enum VBOXSERVICECTRLPIPEID. */
-    uint8_t     uPipeId;
-    /** The data buffer. */
-    uint8_t    *pbData;
-    /** The amount of allocated buffer space. */
-    uint32_t    cbAllocated;
-    /** The actual used/occupied buffer space. */
-    uint32_t    cbSize;
-    /** Helper variable for keeping track of what
-     *  already was processed and what not. */
-    uint32_t    cbOffset;
-    /** Critical section protecting this buffer structure. */
-    RTCRITSECT  CritSect;
-    /** Flag indicating whether this pipe buffer accepts new
-     *  data to be written to or not. If not enabled, already
-     *  (allocated) buffered data still can be read out. */
-    bool        fEnabled;
-    /** Set if it's necessary to write to the notification pipe. */
-    bool        fNeedNotification;
-    /** Set if the pipe needs to be closed after the next read/write. */
-    bool        fPendingClose;
-    /** The notification pipe associated with this buffer.
-     * This is NIL_RTPIPE for output pipes. */
-    RTPIPE      hNotificationPipeW;
-    /** The other end of hNotificationPipeW. */
-    RTPIPE      hNotificationPipeR;
-    /** The event semaphore for getting notified whether something
-     *  has changed, e.g. written to this buffer or enabled/disabled it. */
-    RTSEMEVENT  hEventSem;
-} VBOXSERVICECTRLEXECPIPEBUF;
-/** Pointer to buffered pipe data. */
-typedef VBOXSERVICECTRLEXECPIPEBUF *PVBOXSERVICECTRLEXECPIPEBUF;
-
-/**
- * Structure for holding guest exection relevant data.
- */
-typedef struct
-{
-    uint32_t  uPID;
-    char     *pszCmd;
-    uint32_t  uFlags;
-    char    **papszArgs;
-    uint32_t  uNumArgs;
-    char    **papszEnv;
-    uint32_t  uNumEnvVars;
-    char     *pszUser;
-    char     *pszPassword;
-    uint32_t  uTimeLimitMS;
-
-    RTPIPE                     pipeStdInW;
-    VBOXSERVICECTRLEXECPIPEBUF stdIn;
-    VBOXSERVICECTRLEXECPIPEBUF stdOut;
-    VBOXSERVICECTRLEXECPIPEBUF stdErr;
-
-} VBOXSERVICECTRLTHREADDATAEXEC;
-/** Pointer to thread data. */
-typedef VBOXSERVICECTRLTHREADDATAEXEC *PVBOXSERVICECTRLTHREADDATAEXEC;
-
-/* Structure for holding thread relevant data. */
+ * Request types to perform on a started guest process.
+ */
+typedef enum VBOXSERVICECTRLREQUESTTYPE
+{
+    /** Unknown request. */
+    VBOXSERVICECTRLREQUEST_UNKNOWN          = 0,
+    /** Main control thread asked used to quit. */
+    VBOXSERVICECTRLREQUEST_QUIT             = 1,
+    /** Performs reading from stdout. */
+    VBOXSERVICECTRLREQUEST_STDOUT_READ      = 50,
+    /** Performs reading from stderr. */
+    VBOXSERVICECTRLREQUEST_STDERR_READ      = 60,
+    /** Performs writing to stdin. */
+    VBOXSERVICECTRLREQUEST_STDIN_WRITE      = 70,
+    /** Same as VBOXSERVICECTRLREQUEST_STDIN_WRITE, but
+     *  marks the end of input. */
+    VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF  = 71,
+    /** Kill process.
+     *  @todo Implement this! */
+    VBOXSERVICECTRLREQUEST_KILL             = 90,
+    /** Gently ask process to terminate.
+     *  @todo Implement this! */
+    VBOXSERVICECTRLREQUEST_HANGUP           = 91
+} VBOXSERVICECTRLREQUESTTYPE;
+
+/**
+ * Structure to perform a request on a started guest
+ * process. Needed for letting the main guest control thread
+ * to communicate (and wait) for a certain operation which
+ * will be done in context of the started guest process thread.
+ */
+typedef struct VBOXSERVICECTRLREQUEST
+{
+    /** The request type to handle. */
+    VBOXSERVICECTRLREQUESTTYPE enmType;
+    /** On input, this contains the (maximum) amount
+     *  of buffered data to read or write. On output,
+     *  this show the actual amount of data read/written. */
+    size_t                     cbData;
+    /** The provided, allocated data buffer for input/output. */
+    void                      *pvData;
+    /** The overall result of the operation. */
+    int                        rc;
+} VBOXSERVICECTRLREQUEST;
+/** Pointer to request. */
+typedef VBOXSERVICECTRLREQUEST *PVBOXSERVICECTRLREQUEST;
+
+/**
+ * Structure for holding data for one (started) guest process.
+ */
 typedef struct VBOXSERVICECTRLTHREAD
 {
@@ -206,8 +192,35 @@
     /** Context ID. */
     uint32_t                        uContextID;
-    /** Type of thread.  See VBOXSERVICECTRLTHREADDATATYPE for more info. */
-    VBOXSERVICECTRLTHREADDATATYPE   enmType;
-    /** Pointer to actual thread data, depending on enmType. */
-    void                           *pvData;
+    /** Critical section for thread-safe use. */
+    RTCRITSECT                      CritSect;
+
+    /** @todo Document me! */
+    uint32_t                        uPID;
+    char                           *pszCmd;
+    uint32_t                        uFlags;
+    char                          **papszArgs;
+    uint32_t                        uNumArgs;
+    char                          **papszEnv;
+    uint32_t                        uNumEnvVars;
+    /** Name of specified user account to run the
+     *  guest process under. */
+    char                           *pszUser;
+    /** Password of specified user account. */
+    char                           *pszPassword;
+    /** Overall time limit (in ms) that the guest process
+     *  is allowed to run. 0 for indefinite time. */
+    uint32_t                        uTimeLimitMS;
+    RTSEMEVENTMULTI                 RequestEvent;
+    /** Pointer to the current IPC request being
+     *  processed. */
+    PVBOXSERVICECTRLREQUEST         pRequest;
+    /** StdIn pipe for addressing writes to the
+     *  guest process' stdin.*/
+    RTPIPE                          pipeStdInW;
+    /** The notification pipe associated with this guest process.
+     *  This is NIL_RTPIPE for output pipes. */
+    RTPIPE                          hNotificationPipeW;
+    /** The other end of hNotificationPipeW. */
+    RTPIPE                          hNotificationPipeR;
 } VBOXSERVICECTRLTHREAD;
 /** Pointer to thread data. */
@@ -308,16 +321,19 @@
 
 #ifdef VBOX_WITH_GUEST_CONTROL
-extern void         VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread);
-extern int          VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread);
-
-extern int          VBoxServiceControlExecHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms);
-extern int          VBoxServiceControlExecHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize);
-extern int          VBoxServiceControlExecHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms);
-extern int          VBoxServiceControlExecProcess(uint32_t uClientID, uint32_t uContext,
+/* Guest control functions. */
+extern int          VBoxServiceControlHandleCmdStartProc(uint32_t u32ClientId, uint32_t uNumParms);
+extern int          VBoxServiceControlHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize);
+extern int          VBoxServiceControlHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms);
+extern const PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadByPID(uint32_t uPID);
+extern void         VBoxServiceControlRemoveThread(PVBOXSERVICECTRLTHREAD pThread);
+/* Guest process functions. */
+extern int          VBoxServiceControlThreadStart(uint32_t uClientID, uint32_t uContext,
                                                   const char *pszCmd, uint32_t uFlags,
                                                   const char *pszArgs, uint32_t uNumArgs,
                                                   const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
-                                                  const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS);
-extern void         VBoxServiceControlExecThreadDataDestroy(PVBOXSERVICECTRLTHREADDATAEXEC pThread);
+                                                  const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS,
+                                                  PRTLISTNODE *ppNode);
+extern void         VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread);
+extern int          VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread);
 #endif /* VBOX_WITH_GUEST_CONTROL */
 
Index: trunk/src/VBox/Additions/common/VBoxService/VBoxServicePipeBuf.cpp
===================================================================
--- trunk/src/VBox/Additions/common/VBoxService/VBoxServicePipeBuf.cpp	(revision 39278)
+++ 	(revision )
@@ -1,542 +1,0 @@
-/* $Id$ */
-/** @file
- * VBoxServicePipeBuf - Pipe buffering.
- */
-
-/*
- * Copyright (C) 2010-2011 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-
-/*******************************************************************************
-*   Header Files                                                               *
-*******************************************************************************/
-#include <iprt/assert.h>
-#include <iprt/mem.h>
-#include <iprt/pipe.h>
-#include <iprt/semaphore.h>
-#include <iprt/string.h>
-
-#include "VBoxServicePipeBuf.h"
-
-/**
- * Initializes a pipe buffer.
- *
- * @returns IPRT status code.
- * @param   pBuf                    The pipe buffer to initialize.
- * @param   uId                     The pipe's ID handle.
- * @param   fNeedNotificationPipe   Whether the buffer needs a notification
- *                                  pipe or not.
- */
-int VBoxServicePipeBufInit(PVBOXSERVICECTRLEXECPIPEBUF pBuf, uint8_t uId, bool fNeedNotificationPipe)
-{
-    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
-
-    /** @todo Add allocation size as function parameter! */
-    pBuf->uPID = 0; /* We don't have the PID yet. */
-    pBuf->uPipeId = uId;
-    pBuf->pbData = (uint8_t *)RTMemAlloc(_64K); /* Start with a 64k buffer. */
-    AssertReturn(pBuf->pbData, VERR_NO_MEMORY);
-    pBuf->cbAllocated = _64K;
-    pBuf->cbSize = 0;
-    pBuf->cbOffset = 0;
-    pBuf->fEnabled = true;
-    pBuf->fPendingClose = false;
-    pBuf->fNeedNotification = fNeedNotificationPipe;
-    pBuf->hNotificationPipeW = NIL_RTPIPE;
-    pBuf->hNotificationPipeR = NIL_RTPIPE;
-    pBuf->hEventSem = NIL_RTSEMEVENT;
-
-    int rc = RTSemEventCreate(&pBuf->hEventSem);
-    if (RT_SUCCESS(rc))
-    {
-        rc = RTCritSectInit(&pBuf->CritSect);
-        if (RT_SUCCESS(rc) && fNeedNotificationPipe)
-            rc = RTPipeCreate(&pBuf->hNotificationPipeR, &pBuf->hNotificationPipeW, 0);
-    }
-
-    if (RT_FAILURE(rc))
-    {
-        if (RTCritSectIsInitialized(&pBuf->CritSect))
-            RTCritSectDelete(&pBuf->CritSect);
-        if (pBuf->hEventSem != NIL_RTSEMEVENT)
-            RTSemEventDestroy(pBuf->hEventSem);
-    }
-    return rc;
-}
-
-
-/**
- * Reads out data from a specififed pipe buffer.
- *
- * @return  IPRT status code.
- * @param   pBuf                        Pointer to pipe buffer to read the data from.
- * @param   pbBuffer                    Pointer to buffer to store the read out data.
- * @param   cbBuffer                    Size (in bytes) of the buffer where to store the data.
- * @param   pcbToRead                   Pointer to desired amount (in bytes) of data to read,
- *                                      will reflect the actual amount read on return.
- */
-int VBoxServicePipeBufRead(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
-                           uint8_t *pbBuffer, uint32_t cbBuffer, uint32_t *pcbToRead)
-{
-    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
-    AssertPtrReturn(pbBuffer, VERR_INVALID_POINTER);
-    AssertReturn(cbBuffer, VERR_INVALID_PARAMETER);
-    AssertPtrReturn(pcbToRead, VERR_INVALID_POINTER);
-    AssertReturn(*pcbToRead > 0, VERR_INVALID_PARAMETER); /* Nothing to read makes no sense ... */
-
-    int rc = RTCritSectEnter(&pBuf->CritSect);
-    if (RT_SUCCESS(rc))
-    {
-        Assert(pBuf->cbSize >= pBuf->cbOffset);
-        uint32_t cbToRead = *pcbToRead;
-
-        if (cbToRead > pBuf->cbSize - pBuf->cbOffset)
-            cbToRead = pBuf->cbSize - pBuf->cbOffset;
-
-        if (cbToRead > cbBuffer)
-            cbToRead = cbBuffer;
-
-        if (cbToRead)
-        {
-            memcpy(pbBuffer, &pBuf->pbData[pBuf->cbOffset], cbToRead);
-            pBuf->cbOffset += cbToRead;
-
-#ifdef DEBUG_andy
-            VBoxServiceVerbose(4, "Pipe [%u %u 0x%p %s] read cbToRead=%u, cbSize=%u, cbAlloc=%u, cbOff=%u\n",
-                               pBuf->uPID, pBuf->uPipeId, pBuf, pBuf->fEnabled ? "EN" : "DIS", cbToRead, pBuf->cbSize, pBuf->cbAllocated, pBuf->cbOffset);
-#endif
-            /* Don't signal event semaphore here -- we only read out something from
-             * this pipe buffer. */
-
-            *pcbToRead = cbToRead;
-        }
-        else
-        {
-            *pcbToRead = 0;
-        }
-
-        int rc2 = RTCritSectLeave(&pBuf->CritSect);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-    return rc;
-}
-
-
-/**
- * Peeks for buffer data without moving the buffer's offset
- * or touching any other internal data.
- *
- * @return  IPRT status code.
- * @param   pBuf                        Pointer to pipe buffer to read the data from.
- * @param   pbBuffer                    Pointer to buffer to store the read out data.
- * @param   cbBuffer                    Size (in bytes) of the buffer where to store the data.
- * @param   cbOffset                    Offset (in bytes) where to start reading.
- * @param   pcbRead                     Pointer to desired amount (in bytes) of data to read,
- *                                      will reflect the actual amount read on return.
- * @param   pcbLeft                     Pointer to bytes left in buffer after the current read
- *                                      operation.
- */
-int VBoxServicePipeBufPeek(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
-                           uint8_t *pbBuffer, uint32_t cbBuffer,
-                           uint32_t cbOffset,
-                           uint32_t *pcbRead, uint32_t *pcbLeft)
-{
-    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
-    AssertPtrReturn(pbBuffer, VERR_INVALID_POINTER);
-    AssertReturn(cbBuffer, VERR_INVALID_PARAMETER);
-    AssertReturn(pBuf->cbSize >= pBuf->cbOffset, VERR_BUFFER_OVERFLOW);
-
-    int rc = RTCritSectEnter(&pBuf->CritSect);
-    if (RT_SUCCESS(rc))
-    {
-        if (cbOffset > pBuf->cbSize)
-            cbOffset = pBuf->cbSize;
-        uint32_t cbToRead = pBuf->cbSize - cbOffset;
-        if (cbToRead > cbBuffer)
-            cbToRead = cbBuffer;
-        if (cbToRead)
-        {
-            memcpy(pbBuffer, &pBuf->pbData[cbOffset], cbToRead);
-            pbBuffer[cbBuffer - 1] = '\0';
-        }
-        if (pcbRead)
-            *pcbRead = cbToRead;
-        if (pcbLeft)
-            *pcbLeft = pBuf->cbSize - (cbOffset + cbToRead);
-
-        int rc2 = RTCritSectLeave(&pBuf->CritSect);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-    return rc;
-}
-
-
-/**
- * Writes data into a specififed pipe buffer.
- *
- * @return  IPRT status code.
- * @param   pBuf                        Pointer to pipe buffer to write data into.
- * @param   pbData                      Pointer to byte data to write.
- * @param   cbData                      Data size (in bytes) to write.
- * @param   fPendingClose               Needs the pipe (buffer) to be closed next time we have the chance to?
- * @param   pcbWritten                  Pointer to where the amount of written bytes get stored. Optional.
- */
-int VBoxServicePipeBufWriteToBuf(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
-                                 uint8_t *pbData, uint32_t cbData, bool fPendingClose,
-                                 uint32_t *pcbWritten)
-{
-    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
-    AssertPtrReturn(pbData, VERR_INVALID_POINTER);
-
-    int rc = RTCritSectEnter(&pBuf->CritSect);
-    if (RT_SUCCESS(rc))
-    {
-        if (pBuf->fEnabled)
-        {
-            /* Rewind the buffer if it's empty. */
-            size_t     cbInBuf   = pBuf->cbSize - pBuf->cbOffset;
-            bool const fAddToSet = cbInBuf == 0;
-            if (fAddToSet)
-                pBuf->cbSize = pBuf->cbOffset = 0;
-
-            /* Try and see if we can simply append the data. */
-            if (cbData + pBuf->cbSize <= pBuf->cbAllocated)
-            {
-                memcpy(&pBuf->pbData[pBuf->cbSize], pbData, cbData);
-                pBuf->cbSize += cbData;
-            }
-            else
-            {
-                /* Move any buffered data to the front. */
-                cbInBuf = pBuf->cbSize - pBuf->cbOffset;
-                if (cbInBuf == 0)
-                    pBuf->cbSize = pBuf->cbOffset = 0;
-                else if (pBuf->cbOffset) /* Do we have something to move? */
-                {
-                    memmove(pBuf->pbData, &pBuf->pbData[pBuf->cbOffset], cbInBuf);
-                    pBuf->cbSize = cbInBuf;
-                    pBuf->cbOffset = 0;
-                }
-
-                /* Do we need to grow the buffer? */
-                if (cbData + pBuf->cbSize > pBuf->cbAllocated)
-                {
-                    size_t cbAlloc = pBuf->cbSize + cbData;
-                    cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
-                    void *pvNew = RTMemRealloc(pBuf->pbData, cbAlloc);
-                    if (pvNew)
-                    {
-                        pBuf->pbData = (uint8_t *)pvNew;
-                        pBuf->cbAllocated = cbAlloc;
-                    }
-                    else
-                        rc = VERR_NO_MEMORY;
-                }
-
-                /* Finally, copy the data. */
-                if (RT_SUCCESS(rc))
-                {
-                    if (cbData + pBuf->cbSize <= pBuf->cbAllocated)
-                    {
-                        memcpy(&pBuf->pbData[pBuf->cbSize], pbData, cbData);
-                        pBuf->cbSize += cbData;
-                    }
-                    else
-                        rc = VERR_BUFFER_OVERFLOW;
-                }
-            }
-
-            if (RT_SUCCESS(rc))
-            {
-                /*
-                 * Was this the final read/write to do on this buffer? Then close it
-                 * next time we have the chance to.
-                 */
-                if (fPendingClose)
-                    pBuf->fPendingClose = fPendingClose;
-
-                /*
-                 * Wake up the thread servicing the process so it can feed it
-                 * (if we have a notification helper pipe).
-                 */
-                if (pBuf->fNeedNotification)
-                {
-                    size_t cbWritten;
-                    int rc2 = RTPipeWrite(pBuf->hNotificationPipeW, "i", 1, &cbWritten);
-
-                    /* Disable notification until it is set again on successful write. */
-                    pBuf->fNeedNotification = !RT_SUCCESS(rc2);
-                }
-
-                /* Report back written bytes (if wanted). */
-                if (pcbWritten)
-                    *pcbWritten = cbData;
-
-                /* Only trigger signal if we really wrote something. */
-                if (   cbData
-                    && pBuf->hEventSem != NIL_RTSEMEVENT)
-                {
-                    rc = RTSemEventSignal(pBuf->hEventSem);
-                    AssertRC(rc);
-                }
-
-#ifdef DEBUG_andy
-                VBoxServiceVerbose(4, "Pipe [%u %u 0x%p %s] written cbData=%u, cbSize=%u, cbAlloc=%u, cbOff=%u\n",
-                                   pBuf->uPID, pBuf->uPipeId, pBuf, pBuf->fEnabled ? "EN" : "DIS", cbData, pBuf->cbSize, pBuf->cbAllocated, pBuf->cbOffset);
-#endif
-            }
-        }
-        else
-            rc = VERR_BAD_PIPE;
-
-        int rc2 = RTCritSectLeave(&pBuf->CritSect);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-    return rc;
-}
-
-
-int VBoxServicePipeBufWriteToPipe(PVBOXSERVICECTRLEXECPIPEBUF pBuf, RTPIPE hPipe,
-                                  size_t *pcbWritten, size_t *pcbLeft)
-{
-    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
-    AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
-    AssertPtrReturn(pcbLeft, VERR_INVALID_POINTER);
-
-    int rc = RTCritSectEnter(&pBuf->CritSect);
-    if (RT_SUCCESS(rc))
-    {
-        Assert(pBuf->cbSize >= pBuf->cbOffset);
-        size_t cbToWrite = pBuf->cbSize - pBuf->cbOffset;
-        cbToWrite = RT_MIN(cbToWrite, _64K);
-
-        /* Set current bytes left in pipe buffer. It's okay
-         * to have no data in yet ... */
-        *pcbLeft = pBuf->cbSize - pBuf->cbOffset;
-
-        if (   pBuf->fEnabled
-            && cbToWrite)
-        {
-            rc = RTPipeWrite(hPipe, &pBuf->pbData[pBuf->cbOffset], cbToWrite, pcbWritten);
-            if (RT_SUCCESS(rc))
-            {
-                pBuf->fNeedNotification = true;
-                if (rc != VINF_TRY_AGAIN)
-                    pBuf->cbOffset += *pcbWritten;
-
-                /* Did somebody tell us that we should come to an end,
-                 * e.g. no more data coming in? */
-                if (pBuf->fPendingClose)
-                {
-                    /* Is there more data to write out? */
-                    if (pBuf->cbSize > pBuf->cbOffset)
-                    {
-                        /* We have a pending close, but there's more data that we can write out
-                         * from buffer to pipe ... */
-                        if (pBuf->fNeedNotification)
-                        {
-                            /* Still data to push out - so we need another
-                             * poll round! Write something into the notification pipe. */
-                            size_t cbWrittenIgnore;
-                            int rc2 = RTPipeWrite(pBuf->hNotificationPipeW, "i", 1, &cbWrittenIgnore);
-                            AssertRC(rc2);
-
-                            /* Disable notification until it is set again on successful write. */
-                            pBuf->fNeedNotification = !RT_SUCCESS(rc2);
-                        }
-                    }
-                }
-
-                /* Set new bytes left in pipe buffer afer we wrote to the pipe above. */
-                *pcbLeft = pBuf->cbSize - pBuf->cbOffset;
-            }
-            else
-            {
-                *pcbWritten = 0;
-                /* Don't set pBuf->fEnabled to false here! We just didn't write
-                 * anything -- that doesn't mean this buffer is disabled (again). */
-            }
-        }
-        else
-        {
-            *pcbWritten = 0;
-            pBuf->fNeedNotification = pBuf->fEnabled;
-        }
-
-        int rc2 = RTCritSectLeave(&pBuf->CritSect);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-    return rc;
-}
-
-
-/**
- * Returns whether a pipe buffer is active or not.
- *
- * @return  bool            True if pipe buffer is active, false if not.
- * @param   pBuf            The pipe buffer.
- */
-bool VBoxServicePipeBufIsEnabled(PVBOXSERVICECTRLEXECPIPEBUF pBuf)
-{
-    AssertPtrReturn(pBuf, false);
-
-    bool fEnabled = false;
-    int rc = RTCritSectEnter(&pBuf->CritSect);
-    if (RT_SUCCESS(rc))
-    {
-        fEnabled = pBuf->fEnabled;
-        rc = RTCritSectLeave(&pBuf->CritSect);
-        AssertRC(rc);
-    }
-    return fEnabled;
-}
-
-
-/**
- * Returns whether a pipe buffer is in a pending close state or
- * not. This means that someone has written the last chunk into
- * the pipe buffer with the fPendingClose flag set.
- *
- * @return  bool            True if pipe buffer is in pending
- *                          close state, false if not.
- * @param   pBuf            The pipe buffer.
- */
-bool VBoxServicePipeBufIsClosing(PVBOXSERVICECTRLEXECPIPEBUF pBuf)
-{
-    AssertPtrReturn(pBuf, false);
-
-    if (!RTCritSectIsInitialized(&pBuf->CritSect))
-        return false;
-
-    bool fClosing = false;
-    if (RT_SUCCESS(RTCritSectEnter(&pBuf->CritSect)))
-    {
-        fClosing = pBuf->fPendingClose;
-        RTCritSectLeave(&pBuf->CritSect);
-    }
-    return fClosing;
-}
-
-
-/**
- * Sets the current status (enabled/disabled) of a pipe buffer.
- *
- * @return  IPRT status code.
- * @param   pBuf            The pipe buffer.
- * @param   fEnabled        Pipe buffer status to set.
- */
-int VBoxServicePipeBufSetStatus(PVBOXSERVICECTRLEXECPIPEBUF pBuf, bool fEnabled)
-{
-    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
-
-    int rc = RTCritSectEnter(&pBuf->CritSect);
-    if (RT_SUCCESS(rc))
-    {
-        bool fEnabledOld = pBuf->fEnabled;
-        pBuf->fEnabled = fEnabled;
-        if (   pBuf->fEnabled != fEnabledOld
-            && pBuf->hEventSem)
-        {
-#ifdef DEBUG_andy
-            VBoxServiceVerbose(4, "Pipe [%u %u] status %s -> %s\n",
-                               pBuf->uPID, pBuf->uPipeId,
-                               fEnabledOld ? "EN" : "DIS", pBuf->fEnabled ? "EN" : "DIS");
-#endif
-            /* Let waiter know that something has changed ... */
-            RTSemEventSignal(pBuf->hEventSem);
-        }
-        rc = RTCritSectLeave(&pBuf->CritSect);
-    }
-    return rc;
-}
-
-
-/**
- * Assigns a PID to the specified pipe buffer. Does not allow
- * setting a new PID after a PID already was set.
- *
- * @return  IPRT status code.
- * @param   pBuf            The pipe buffer.
- * @param   uPID            PID to assign.
- */
-int VBoxServicePipeBufSetPID(PVBOXSERVICECTRLEXECPIPEBUF pBuf, uint32_t uPID)
-{
-    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
-
-    int rc = RTCritSectEnter(&pBuf->CritSect);
-    if (RT_SUCCESS(rc))
-    {
-        if (!pBuf->uPID)
-            pBuf->uPID = uPID;
-        rc = RTCritSectLeave(&pBuf->CritSect);
-    }
-    return rc;
-}
-
-
-/**
- * Waits for the pipe buffer to get notified when something changed, like
- * new data was written to or the buffer was disabled, i.e. not used anymore.
- *
- * @return  IPRT status code.
- * @param   pBuf            The pipe buffer.
- * @param   cMillies        The maximum time (in ms) to wait for the event.
- */
-int VBoxServicePipeBufWaitForEvent(PVBOXSERVICECTRLEXECPIPEBUF pBuf, RTMSINTERVAL cMillies)
-{
-    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
-
-    /* Don't enter the critical section here; someone else could signal the event semaphore
-     * and this will deadlock then ... */
-    return RTSemEventWait(pBuf->hEventSem, cMillies);
-}
-
-
-/**
- * Deletes a pipe buffer.
- *
- * @note    Not thread safe -- only call this when nobody is relying on the
- *          data anymore!
- *
- * @param   pBuf            The pipe buffer.
- */
-void VBoxServicePipeBufDestroy(PVBOXSERVICECTRLEXECPIPEBUF pBuf)
-{
-    AssertPtrReturnVoid(pBuf);
-
-    if (pBuf->pbData)
-    {
-        pBuf->uPID = 0;
-        pBuf->uPipeId = 0;
-        pBuf->cbAllocated = 0;
-        pBuf->cbSize = 0;
-        pBuf->cbOffset = 0;
-
-        RTPipeClose(pBuf->hNotificationPipeR);
-        pBuf->hNotificationPipeR = NIL_RTPIPE;
-        RTPipeClose(pBuf->hNotificationPipeW);
-        pBuf->hNotificationPipeW = NIL_RTPIPE;
-
-        if (RTCritSectIsInitialized(&pBuf->CritSect))
-            RTCritSectDelete(&pBuf->CritSect);
-        if (pBuf->hEventSem != NIL_RTSEMEVENT)
-            RTSemEventDestroy(pBuf->hEventSem);
-
-        RTMemFree(pBuf->pbData);
-        pBuf->pbData = NULL;
-    }
-}
-
Index: trunk/src/VBox/Additions/common/VBoxService/VBoxServicePipeBuf.h
===================================================================
--- trunk/src/VBox/Additions/common/VBoxService/VBoxServicePipeBuf.h	(revision 39278)
+++ 	(revision )
@@ -1,42 +1,0 @@
-/* $Id$ */
-/** @file
- * VBoxServicePipeBuf - Pipe buffering.
- */
-
-/*
- * Copyright (C) 2010-2011 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-#ifndef ___VBoxServicePipeBuf_h
-#define ___VBoxServicePipeBuf_h
-
-#include "VBoxServiceInternal.h"
-
-int  VBoxServicePipeBufInit(PVBOXSERVICECTRLEXECPIPEBUF pBuf, uint8_t uId, bool fNeedNotificationPipe);
-int  VBoxServicePipeBufRead(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
-                            uint8_t *pbBuffer, uint32_t cbBuffer, uint32_t *pcbToRead);
-int VBoxServicePipeBufPeek(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
-                           uint8_t *pbBuffer, uint32_t cbBuffer,
-                           uint32_t cbOffset,
-                           uint32_t *pcbRead, uint32_t *pcbLeft);
-int  VBoxServicePipeBufWriteToBuf(PVBOXSERVICECTRLEXECPIPEBUF pBuf,
-                                  uint8_t *pbData, uint32_t cbData, bool fPendingClose, uint32_t *pcbWritten);
-int  VBoxServicePipeBufWriteToPipe(PVBOXSERVICECTRLEXECPIPEBUF pBuf, RTPIPE hPipe,
-                                   size_t *pcbWritten, size_t *pcbLeft);
-bool VBoxServicePipeBufIsEnabled(PVBOXSERVICECTRLEXECPIPEBUF pBuf);
-bool VBoxServicePipeBufIsClosing(PVBOXSERVICECTRLEXECPIPEBUF pBuf);
-int  VBoxServicePipeBufSetPID(PVBOXSERVICECTRLEXECPIPEBUF pBuf, uint32_t uPID);
-int  VBoxServicePipeBufSetStatus(PVBOXSERVICECTRLEXECPIPEBUF pBuf, bool fEnabled);
-int  VBoxServicePipeBufWaitForEvent(PVBOXSERVICECTRLEXECPIPEBUF pBuf, RTMSINTERVAL cMillies);
-void VBoxServicePipeBufDestroy(PVBOXSERVICECTRLEXECPIPEBUF pBuf);
-
-#endif  /* !___VBoxServicePipeBuf_h */
-
Index: trunk/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp
===================================================================
--- trunk/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp	(revision 39278)
+++ trunk/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp	(revision 39279)
@@ -674,6 +674,26 @@
         { szSysDir, "VBoxCredProv.dll" },
 
- /* On 64-bit we don't yet have the OpenGL DLLs in native format.
-    So just enumerate the 32-bit files in the SYSWOW directory. */
+        /* Handy-to-know stuff, mostly patched stuff. */
+        { szSysDir, "libWine.dll" },
+        { szSysDir, "wined3d.dll" },
+        { szSysDir, "d3d8.dll" },
+        { szSysDir, "d3d9.dll" },
+
+        /* Direct3D. */
+        { szSysDir, "VBoxDispD3D.dll" },
+        { szSysDir, "VBoxD3D8.dll" },
+        { szSysDir, "VBoxD3D9.dll" },
+
+        /* OpenGL. */
+        { szSysDir, "VBoxOGLarrayspu.dll" },
+        { szSysDir, "VBoxOGLcrutil.dll" },
+        { szSysDir, "VBoxOGLerrorspu.dll" },
+        { szSysDir, "VBoxOGLpackspu.dll" },
+        { szSysDir, "VBoxOGLpassthroughspu.dll" },
+        { szSysDir, "VBoxOGLfeedbackspu.dll" },
+        { szSysDir, "VBoxOGL.dll" },
+
+        /* On 64-bit OSes we also have our OpenGL + D3D DLLs in 32-bit,
+           so enumerate these files in the SYSWOW directory. */
 # ifdef RT_ARCH_AMD64
         { szSysWowDir, "VBoxOGLarrayspu.dll" },
@@ -684,18 +704,14 @@
         { szSysWowDir, "VBoxOGLfeedbackspu.dll" },
         { szSysWowDir, "VBoxOGL.dll" },
-# else  /* !RT_ARCH_AMD64 */
-        { szSysDir, "VBoxOGLarrayspu.dll" },
-        { szSysDir, "VBoxOGLcrutil.dll" },
-        { szSysDir, "VBoxOGLerrorspu.dll" },
-        { szSysDir, "VBoxOGLpackspu.dll" },
-        { szSysDir, "VBoxOGLpassthroughspu.dll" },
-        { szSysDir, "VBoxOGLfeedbackspu.dll" },
-        { szSysDir, "VBoxOGL.dll" },
-# endif /* !RT_ARCH_AMD64 */
-
+
+        { szSysWowDir, "VBoxDispD3D.dll" },
+# endif /* RT_ARCH_AMD64 */
+
+        /* Installed drivers. */
         { szDriversDir, "VBoxGuest.sys" },
         { szDriversDir, "VBoxMouse.sys" },
         { szDriversDir, "VBoxSF.sys"    },
         { szDriversDir, "VBoxVideo.sys" },
+        { szDriversDir, "VBoxVideoWddm.sys" },
     };
 
