summaryrefslogtreecommitdiff
path: root/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2014-03-26 19:21:20 +0000
committer <>2014-05-08 15:03:54 +0000
commitfb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch)
treec2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp
parent58ed4748338f9466599adfc8a9171280ed99e23f (diff)
downloadVirtualBox-master.tar.gz
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp')
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp1794
1 files changed, 0 insertions, 1794 deletions
diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp
deleted file mode 100644
index 78ec20a8..00000000
--- a/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp
+++ /dev/null
@@ -1,1794 +0,0 @@
-/* $Id: VBoxServiceControlThread.cpp $ */
-/** @file
- * VBoxServiceControlExecThread - Thread for every started guest process.
- */
-
-/*
- * Copyright (C) 2012 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/asm.h>
-#include <iprt/assert.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/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 "VBoxServiceInternal.h"
-
-using namespace guestControl;
-
-/* Internal functions. */
-static int vboxServiceControlThreadRequestCancel(PVBOXSERVICECTRLREQUEST pThread);
-
-/**
- * Initialies the passed in thread data structure with the parameters given.
- *
- * @return IPRT status code.
- * @param pThread The thread's handle to allocate the data for.
- * @param u32ContextID The context ID bound to this request / command.
- @ @param pProcess Process information.
- */
-static int gstsvcCntlExecThreadInit(PVBOXSERVICECTRLTHREAD pThread,
- PVBOXSERVICECTRLPROCESS pProcess,
- uint32_t u32ContextID)
-{
- AssertPtrReturn(pThread, VERR_INVALID_POINTER);
- AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
-
- /* General stuff. */
- pThread->pAnchor = NULL;
- pThread->Node.pPrev = NULL;
- pThread->Node.pNext = NULL;
-
- pThread->fShutdown = false;
- pThread->fStarted = false;
- pThread->fStopped = false;
-
- pThread->uContextID = u32ContextID;
- /* ClientID will be assigned when thread is started; every guest
- * process has its own client ID to detect crashes on a per-guest-process
- * level. */
-
- int rc = RTCritSectInit(&pThread->CritSect);
- if (RT_FAILURE(rc))
- return rc;
-
- pThread->uPID = 0; /* Don't have a PID yet. */
- pThread->pRequest = NULL; /* No request assigned yet. */
- pThread->uFlags = pProcess->uFlags;
- pThread->uTimeLimitMS = ( pProcess->uTimeLimitMS == UINT32_MAX
- || pProcess->uTimeLimitMS == 0)
- ? RT_INDEFINITE_WAIT : pProcess->uTimeLimitMS;
-
- /* Prepare argument list. */
- pThread->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
- rc = RTGetOptArgvFromString(&pThread->papszArgs, (int*)&pThread->uNumArgs,
- (pProcess->uNumArgs > 0) ? pProcess->szArgs : "", NULL);
- /* Did we get the same result? */
- Assert(pProcess->uNumArgs == pThread->uNumArgs);
-
- if (RT_SUCCESS(rc))
- {
- /* Prepare environment list. */
- pThread->uNumEnvVars = 0;
- if (pProcess->uNumEnvVars)
- {
- pThread->papszEnv = (char **)RTMemAlloc(pProcess->uNumEnvVars * sizeof(char*));
- AssertPtr(pThread->papszEnv);
- pThread->uNumEnvVars = pProcess->uNumEnvVars;
-
- const char *pszCur = pProcess->szEnv;
- uint32_t i = 0;
- uint32_t cbLen = 0;
- while (cbLen < pProcess->cbEnv)
- {
- /* sanity check */
- if (i >= pProcess->uNumEnvVars)
- {
- rc = VERR_INVALID_PARAMETER;
- break;
- }
- int cbStr = RTStrAPrintf(&pThread->papszEnv[i++], "%s", pszCur);
- if (cbStr < 0)
- {
- rc = VERR_NO_STR_MEMORY;
- break;
- }
- pszCur += cbStr + 1; /* Skip terminating '\0' */
- cbLen += cbStr + 1; /* Skip terminating '\0' */
- }
- Assert(i == pThread->uNumEnvVars);
- }
-
- /* The actual command to execute. */
- pThread->pszCmd = RTStrDup(pProcess->szCmd);
- AssertPtr(pThread->pszCmd);
-
- /* User management. */
- pThread->pszUser = RTStrDup(pProcess->szUser);
- AssertPtr(pThread->pszUser);
- pThread->pszPassword = RTStrDup(pProcess->szPassword);
- AssertPtr(pThread->pszPassword);
- }
-
- if (RT_FAILURE(rc)) /* Clean up on failure. */
- VBoxServiceControlThreadFree(pThread);
- return rc;
-}
-
-
-/**
- * Frees a guest thread.
- *
- * @return IPRT status code.
- * @param pThread Thread to shut down.
- */
-int VBoxServiceControlThreadFree(PVBOXSERVICECTRLTHREAD pThread)
-{
- AssertPtrReturn(pThread, VERR_INVALID_POINTER);
-
- VBoxServiceVerbose(3, "[PID %u]: Freeing ...\n",
- pThread->uPID);
-
- int rc = RTCritSectEnter(&pThread->CritSect);
- if (RT_SUCCESS(rc))
- {
- if (pThread->uNumEnvVars)
- {
- for (uint32_t i = 0; i < pThread->uNumEnvVars; i++)
- RTStrFree(pThread->papszEnv[i]);
- RTMemFree(pThread->papszEnv);
- }
- RTGetOptArgvFree(pThread->papszArgs);
-
- RTStrFree(pThread->pszCmd);
- RTStrFree(pThread->pszUser);
- RTStrFree(pThread->pszPassword);
-
- VBoxServiceVerbose(3, "[PID %u]: Setting stopped state\n",
- pThread->uPID);
-
- rc = RTCritSectLeave(&pThread->CritSect);
- AssertRC(rc);
- }
-
- /*
- * Destroy other thread data.
- */
- if (RTCritSectIsInitialized(&pThread->CritSect))
- RTCritSectDelete(&pThread->CritSect);
-
- /*
- * Destroy thread structure as final step.
- */
- RTMemFree(pThread);
- pThread = NULL;
-
- return rc;
-}
-
-
-/**
- * Signals a guest process thread that we want it to shut down in
- * a gentle way.
- *
- * @return IPRT status code.
- * @param pThread Thread to shut down.
- */
-int VBoxServiceControlThreadStop(const PVBOXSERVICECTRLTHREAD pThread)
-{
- AssertPtrReturn(pThread, VERR_INVALID_POINTER);
-
- VBoxServiceVerbose(3, "[PID %u]: Stopping ...\n",
- pThread->uPID);
-
- int rc = vboxServiceControlThreadRequestCancel(pThread->pRequest);
- if (RT_FAILURE(rc))
- VBoxServiceError("[PID %u]: Signalling request event failed, rc=%Rrc\n",
- pThread->uPID, rc);
-
- /* Do *not* set pThread->fShutdown or other stuff here!
- * The guest thread loop will do that as soon as it processes the quit message. */
-
- PVBOXSERVICECTRLREQUEST pRequest;
- rc = VBoxServiceControlThreadRequestAlloc(&pRequest, VBOXSERVICECTRLREQUEST_QUIT);
- if (RT_SUCCESS(rc))
- {
- rc = VBoxServiceControlThreadPerform(pThread->uPID, pRequest);
- if (RT_FAILURE(rc))
- VBoxServiceVerbose(3, "[PID %u]: Sending quit request failed with rc=%Rrc\n",
- pThread->uPID, rc);
-
- VBoxServiceControlThreadRequestFree(pRequest);
- }
- return rc;
-}
-
-
-/**
- * Wait for a guest process thread to shut down.
- *
- * @return IPRT status code.
- * @param pThread Thread to wait shutting down for.
- * @param RTMSINTERVAL Timeout in ms to wait for shutdown.
- * @param prc Where to store the thread's return code. Optional.
- */
-int VBoxServiceControlThreadWait(const PVBOXSERVICECTRLTHREAD pThread,
- RTMSINTERVAL msTimeout, int *prc)
-{
- AssertPtrReturn(pThread, VERR_INVALID_POINTER);
- /* prc is optional. */
-
- int rc = VINF_SUCCESS;
- if ( pThread->Thread != NIL_RTTHREAD
- && ASMAtomicReadBool(&pThread->fStarted))
- {
- VBoxServiceVerbose(2, "[PID %u]: Waiting for shutdown of pThread=0x%p = \"%s\"...\n",
- pThread->uPID, pThread, pThread->pszCmd);
-
- /* Wait a bit ... */
- int rcThread;
- rc = RTThreadWait(pThread->Thread, msTimeout, &rcThread);
- if (RT_FAILURE(rc))
- {
- VBoxServiceError("[PID %u]: Waiting for shutting down thread returned error rc=%Rrc\n",
- pThread->uPID, rc);
- }
- else
- {
- VBoxServiceVerbose(3, "[PID %u]: Thread reported exit code=%Rrc\n",
- pThread->uPID, rcThread);
- if (prc)
- *prc = rcThread;
- }
- }
- return rc;
-}
-
-
-/**
- * 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 VBoxServiceControlThreadCloseStdIn(RTPOLLSET hPollSet, PRTPIPE phStdInW)
-{
- AssertPtrReturn(phStdInW, VERR_INVALID_POINTER);
-
- int rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN);
- if (rc != VERR_POLL_HANDLE_ID_NOT_FOUND)
- AssertRC(rc);
-
- if (*phStdInW != NIL_RTPIPE)
- {
- 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 VBoxServiceControlThreadHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW)
-{
- NOREF(fPollEvt);
-
- return VBoxServiceControlThreadCloseStdIn(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 VBoxServiceControlThreadHandleOutputError(RTPOLLSET hPollSet, uint32_t fPollEvt,
- PRTPIPE phPipeR, uint32_t idPollHnd)
-{
- AssertPtrReturn(phPipeR, VERR_INVALID_POINTER);
-
-#ifdef DEBUG
- VBoxServiceVerbose(4, "VBoxServiceControlThreadHandleOutputError: fPollEvt=0x%x, idPollHnd=%u\n",
- fPollEvt, idPollHnd);
-#endif
-
- /* Remove pipe from poll set. */
- int rc2 = RTPollSetRemove(hPollSet, idPollHnd);
- AssertMsg(RT_SUCCESS(rc2) || rc2 == VERR_POLL_HANDLE_ID_NOT_FOUND, ("%Rrc\n", rc2));
-
- bool fClosePipe = true; /* By default close the pipe. */
-
- /* Check if there's remaining data to read from the pipe. */
- size_t cbReadable;
- rc2 = RTPipeQueryReadable(*phPipeR, &cbReadable);
- if ( RT_SUCCESS(rc2)
- && cbReadable)
- {
- VBoxServiceVerbose(3, "VBoxServiceControlThreadHandleOutputError: idPollHnd=%u has %ld bytes left, vetoing close\n",
- idPollHnd, cbReadable);
-
- /* Veto closing the pipe yet because there's still stuff to read
- * from the pipe. This can happen on UNIX-y systems where on
- * error/hangup there still can be data to be read out. */
- fClosePipe = false;
- }
- else
- VBoxServiceVerbose(3, "VBoxServiceControlThreadHandleOutputError: idPollHnd=%u will be closed\n",
- idPollHnd);
-
- if ( *phPipeR != NIL_RTPIPE
- && fClosePipe)
- {
- rc2 = RTPipeClose(*phPipeR);
- AssertRC(rc2);
- *phPipeR = NIL_RTPIPE;
- }
-
- return VINF_SUCCESS;
-}
-
-
-/**
- * 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 VBoxServiceControlThreadHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt,
- PRTPIPE phPipeR, uint32_t idPollHnd)
-{
-#if 0
- VBoxServiceVerbose(4, "VBoxServiceControlThreadHandleOutputEvent: fPollEvt=0x%x, idPollHnd=%u\n",
- fPollEvt, idPollHnd);
-#endif
-
- int rc = VINF_SUCCESS;
-
-#ifdef DEBUG
- size_t cbReadable;
- rc = RTPipeQueryReadable(*phPipeR, &cbReadable);
- if ( RT_SUCCESS(rc)
- && cbReadable)
- {
- VBoxServiceVerbose(4, "VBoxServiceControlThreadHandleOutputEvent: cbReadable=%ld\n",
- cbReadable);
- }
-#endif
-
-#if 0
- //if (fPollEvt & RTPOLL_EVT_READ)
- {
- size_t cbRead = 0;
- uint8_t byData[_64K];
- rc = RTPipeRead(*phPipeR,
- byData, sizeof(byData), &cbRead);
- VBoxServiceVerbose(4, "VBoxServiceControlThreadHandleOutputEvent cbRead=%u, rc=%Rrc\n",
- cbRead, rc);
-
- /* Make sure we go another poll round in case there was too much data
- for the buffer to hold. */
- fPollEvt &= RTPOLL_EVT_ERROR;
- }
-#endif
-
- if (fPollEvt & RTPOLL_EVT_ERROR)
- rc = VBoxServiceControlThreadHandleOutputError(hPollSet, fPollEvt,
- phPipeR, idPollHnd);
- return rc;
-}
-
-
-static int VBoxServiceControlThreadHandleRequest(RTPOLLSET hPollSet, uint32_t fPollEvt,
- PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR,
- PVBOXSERVICECTRLTHREAD pThread)
-{
- AssertPtrReturn(pThread, VERR_INVALID_POINTER);
- AssertPtrReturn(phStdInW, VERR_INVALID_POINTER);
- AssertPtrReturn(phStdOutR, VERR_INVALID_POINTER);
- AssertPtrReturn(phStdErrR, VERR_INVALID_POINTER);
-
- /* Drain the notification pipe. */
- uint8_t abBuf[8];
- size_t cbIgnore;
- int rc = RTPipeRead(pThread->hNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
- if (RT_FAILURE(rc))
- VBoxServiceError("Draining IPC notification pipe failed with rc=%Rrc\n", rc);
-
- int rcReq = VINF_SUCCESS; /* Actual request result. */
-
- PVBOXSERVICECTRLREQUEST pRequest = pThread->pRequest;
- if (!pRequest)
- {
- VBoxServiceError("IPC request is invalid\n");
- return VERR_INVALID_POINTER;
- }
-
- switch (pRequest->enmType)
- {
- case VBOXSERVICECTRLREQUEST_QUIT: /* Main control asked us to quit. */
- {
- /** @todo Check for some conditions to check to
- * veto quitting. */
- ASMAtomicXchgBool(&pThread->fShutdown, true);
- rcReq = VERR_CANCELLED;
- break;
- }
-
- case VBOXSERVICECTRLREQUEST_STDIN_WRITE:
- case VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF:
- {
- size_t cbWritten = 0;
- if (pRequest->cbData)
- {
- AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER);
- if (*phStdInW != NIL_RTPIPE)
- {
- rcReq = RTPipeWrite(*phStdInW,
- pRequest->pvData, pRequest->cbData, &cbWritten);
- }
- else
- rcReq = VINF_EOF;
- }
-
- /*
- * If this is the last write + we have really have written all data
- * we need to close the stdin pipe on our end and remove it from
- * the poll set.
- */
- if ( pRequest->enmType == VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF
- && pRequest->cbData == cbWritten)
- {
- rc = VBoxServiceControlThreadCloseStdIn(hPollSet, phStdInW);
- }
-
- /* Report back actual data written (if any). */
- pRequest->cbData = cbWritten;
- break;
- }
-
- case VBOXSERVICECTRLREQUEST_STDOUT_READ:
- 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 (RT_FAILURE(rcReq))
- {
- RTPollSetRemove(hPollSet, pRequest->enmType == VBOXSERVICECTRLREQUEST_STDERR_READ
- ? VBOXSERVICECTRLPIPEID_STDERR : VBOXSERVICECTRLPIPEID_STDOUT);
- RTPipeClose(*pPipeR);
- *pPipeR = NIL_RTPIPE;
- 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_NOT_IMPLEMENTED;
- break;
- }
-
- /* Assign overall result. */
- pRequest->rc = RT_SUCCESS(rc)
- ? rcReq : rc;
-
- VBoxServiceVerbose(2, "[PID %u]: Handled req=%u, CID=%u, rc=%Rrc, cbData=%u\n",
- pThread->uPID, pRequest->enmType, pRequest->uCID, pRequest->rc, pRequest->cbData);
-
- /* In any case, regardless of the result, we notify
- * the main guest control to unblock it. */
- int rc2 = RTSemEventMultiSignal(pRequest->Event);
- AssertRC(rc2);
-
- /* No access to pRequest here anymore -- could be out of scope
- * or modified already! */
- pThread->pRequest = pRequest = NULL;
-
- 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 VBoxServiceControlThreadProcLoop(PVBOXSERVICECTRLTHREAD pThread,
- RTPROCESS hProcess, RTMSINTERVAL cMsTimeout, RTPOLLSET hPollSet,
- PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR)
-{
- AssertPtrReturn(pThread, VERR_INVALID_POINTER);
- AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER);
- /* Rest is optional. */
-
- 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 = VBoxServiceControlAssignPID(pThread, hProcess);
- if (RT_FAILURE(rc))
- {
- VBoxServiceError("Unable to assign PID=%u, to new thread, rc=%Rrc\n",
- hProcess, 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(2, "[PID %u]: Process \"%s\" started, CID=%u, User=%s\n",
- pThread->uPID, pThread->pszCmd, 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, &fPollEvt, &idPollHnd);
- if (pThread->fShutdown)
- continue;
-
- cMsPollCur = 0; /* No rest until we've checked everything. */
-
- if (RT_SUCCESS(rc2))
- {
- /*VBoxServiceVerbose(4, "[PID %u}: RTPollNoResume idPollHnd=%u\n",
- pThread->uPID, idPollHnd);*/
- switch (idPollHnd)
- {
- case VBOXSERVICECTRLPIPEID_STDIN:
- rc = VBoxServiceControlThreadHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW);
- break;
-
- case VBOXSERVICECTRLPIPEID_STDOUT:
- rc = VBoxServiceControlThreadHandleOutputEvent(hPollSet, fPollEvt,
- phStdOutR, idPollHnd);
- break;
-
- case VBOXSERVICECTRLPIPEID_STDERR:
- rc = VBoxServiceControlThreadHandleOutputEvent(hPollSet, fPollEvt,
- phStdErrR, idPollHnd);
- break;
-
- case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
- rc = VBoxServiceControlThreadHandleRequest(hPollSet, fPollEvt,
- phStdInW, phStdOutR, phStdErrR, pThread);
- break;
- }
-
- if (RT_FAILURE(rc) || rc == VINF_EOF)
- break; /* Abort command, or client dead or something. */
-
- if (RT_UNLIKELY(pThread->fShutdown))
- break; /* We were asked to shutdown. */
-
- continue;
- }
-
-#if 0
- VBoxServiceVerbose(4, "[PID %u]: Polling done, pollRC=%Rrc, pollCnt=%u, rc=%Rrc, fShutdown=%RTbool\n",
- pThread->uPID, rc2, RTPollSetGetCount(hPollSet), rc, pThread->fShutdown);
-#endif
- /*
- * 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 and all output has been consumed,
- * we should be heading out.
- */
- if ( !fProcessAlive
- && *phStdOutR == NIL_RTPIPE
- && *phStdErrR == NIL_RTPIPE)
- 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, "[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 = fProcessAlive
- ? cMsPollBase
- : RT_MS_1MIN;
- if (cMilliesLeft < cMsPollCur)
- cMsPollCur = cMilliesLeft;
-
- /*
- * Need to exit?
- */
- if (pThread->fShutdown)
- break;
- }
-
- rc2 = RTCritSectEnter(&pThread->CritSect);
- if (RT_SUCCESS(rc2))
- {
- ASMAtomicXchgBool(&pThread->fShutdown, true);
-
- rc2 = RTCritSectLeave(&pThread->CritSect);
- AssertRC(rc2);
- }
-
- /*
- * Try kill the process if it's still alive at this point.
- */
- if (fProcessAlive)
- {
- if (MsProcessKilled == UINT64_MAX)
- {
- VBoxServiceVerbose(3, "[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, "[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, "[PID %u]: Kill attempt %d/10: Exited\n",
- pThread->uPID, i + 1);
- fProcessAlive = false;
- break;
- }
- if (i >= 5)
- {
- VBoxServiceVerbose(4, "[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, "[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))
- {
- uint32_t uStatus = PROC_STS_UNDEFINED;
- uint32_t uFlags = 0;
-
- if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX)
- {
- VBoxServiceVerbose(3, "[PID %u]: Timed out and got killed\n",
- pThread->uPID);
- uStatus = PROC_STS_TOK;
- }
- else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX)
- {
- VBoxServiceVerbose(3, "[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, "[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("[PID %u]: Is alive when it should not!\n",
- pThread->uPID);
- }
- else if (MsProcessKilled != UINT64_MAX)
- {
- VBoxServiceError("[PID %u]: Has been killed when it should not!\n",
- pThread->uPID);
- }
- else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
- {
- VBoxServiceVerbose(3, "[PID %u]: Ended with RTPROCEXITREASON_NORMAL (Exit code: %u)\n",
- pThread->uPID, ProcessStatus.iStatus);
-
- uStatus = PROC_STS_TEN;
- uFlags = ProcessStatus.iStatus;
- }
- else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
- {
- VBoxServiceVerbose(3, "[PID %u]: Ended with RTPROCEXITREASON_SIGNAL (Signal: %u)\n",
- pThread->uPID, ProcessStatus.iStatus);
-
- uStatus = PROC_STS_TES;
- uFlags = ProcessStatus.iStatus;
- }
- else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
- {
- /* ProcessStatus.iStatus will be undefined. */
- VBoxServiceVerbose(3, "[PID %u]: Ended with RTPROCEXITREASON_ABEND\n",
- pThread->uPID);
-
- uStatus = PROC_STS_TEA;
- uFlags = ProcessStatus.iStatus;
- }
- else
- VBoxServiceVerbose(1, "[PID %u]: Handling process status %u not implemented\n",
- pThread->uPID, ProcessStatus.enmReason);
-
- VBoxServiceVerbose(2, "[PID %u]: Ended, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n",
- pThread->uPID, pThread->uClientID, pThread->uContextID, uStatus, uFlags);
- rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
- pThread->uPID, uStatus, uFlags,
- NULL /* pvData */, 0 /* cbData */);
- if (RT_FAILURE(rc))
- VBoxServiceError("[PID %u]: Error reporting final status to host; rc=%Rrc\n",
- pThread->uPID, rc);
-
- VBoxServiceVerbose(3, "[PID %u]: Process loop ended with rc=%Rrc\n",
- pThread->uPID, rc);
- }
- else
- VBoxServiceError("[PID %u]: Loop failed with rc=%Rrc\n",
- pThread->uPID, rc);
- return rc;
-}
-
-
-/**
- * Initializes a pipe's handle and pipe object.
- *
- * @return IPRT status code.
- * @param ph The pipe's handle to initialize.
- * @param phPipe The pipe's object to initialize.
- */
-static int vboxServiceControlThreadInitPipe(PRTHANDLE ph, PRTPIPE phPipe)
-{
- AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
- AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
-
- ph->enmType = RTHANDLETYPE_PIPE;
- ph->u.hPipe = NIL_RTPIPE;
- *phPipe = NIL_RTPIPE;
-
- return VINF_SUCCESS;
-}
-
-
-/**
- * Allocates a guest thread request with the specified request data.
- *
- * @return IPRT status code.
- * @param ppReq Pointer that will receive the newly allocated request.
- * Must be freed later with VBoxServiceControlThreadRequestFree().
- * @param enmType Request type.
- * @param pbData Payload data, based on request type.
- * @param cbData Size of payload data (in bytes).
- * @param uCID Context ID to which this request belongs to.
- */
-int VBoxServiceControlThreadRequestAllocEx(PVBOXSERVICECTRLREQUEST *ppReq,
- VBOXSERVICECTRLREQUESTTYPE enmType,
- void* pbData,
- size_t cbData,
- uint32_t uCID)
-{
- AssertPtrReturn(ppReq, VERR_INVALID_POINTER);
-
- PVBOXSERVICECTRLREQUEST pReq = (PVBOXSERVICECTRLREQUEST)
- RTMemAlloc(sizeof(VBOXSERVICECTRLREQUEST));
- AssertPtrReturn(pReq, VERR_NO_MEMORY);
-
- RT_ZERO(*pReq);
- pReq->enmType = enmType;
- pReq->uCID = uCID;
- pReq->cbData = cbData;
- pReq->pvData = (uint8_t*)pbData;
-
- /* Set request result to some defined state in case
- * it got cancelled. */
- pReq->rc = VERR_CANCELLED;
-
- int rc = RTSemEventMultiCreate(&pReq->Event);
- AssertRC(rc);
-
- if (RT_SUCCESS(rc))
- {
- *ppReq = pReq;
- return VINF_SUCCESS;
- }
-
- RTMemFree(pReq);
- return rc;
-}
-
-
-/**
- * Allocates a guest thread request with the specified request data.
- *
- * @return IPRT status code.
- * @param ppReq Pointer that will receive the newly allocated request.
- * Must be freed later with VBoxServiceControlThreadRequestFree().
- * @param enmType Request type.
- */
-int VBoxServiceControlThreadRequestAlloc(PVBOXSERVICECTRLREQUEST *ppReq,
- VBOXSERVICECTRLREQUESTTYPE enmType)
-{
- return VBoxServiceControlThreadRequestAllocEx(ppReq, enmType,
- NULL /* pvData */, 0 /* cbData */,
- 0 /* ContextID */);
-}
-
-
-/**
- * Cancels a previously fired off guest thread request.
- *
- * Note: Does *not* do locking since VBoxServiceControlThreadRequestWait()
- * holds the lock (critsect); so only trigger the signal; the owner
- * needs to clean up afterwards.
- *
- * @return IPRT status code.
- * @param pReq Request to cancel.
- */
-static int vboxServiceControlThreadRequestCancel(PVBOXSERVICECTRLREQUEST pReq)
-{
- if (!pReq) /* Silently skip non-initialized requests. */
- return VINF_SUCCESS;
-
- VBoxServiceVerbose(4, "Cancelling request=0x%p\n", pReq);
-
- return RTSemEventMultiSignal(pReq->Event);
-}
-
-
-/**
- * Frees a formerly allocated guest thread request.
- *
- * @return IPRT status code.
- * @param pReq Request to free.
- */
-void VBoxServiceControlThreadRequestFree(PVBOXSERVICECTRLREQUEST pReq)
-{
- AssertPtrReturnVoid(pReq);
-
- VBoxServiceVerbose(4, "Freeing request=0x%p (event=%RTsem)\n",
- pReq, &pReq->Event);
-
- int rc = RTSemEventMultiDestroy(pReq->Event);
- AssertRC(rc);
-
- RTMemFree(pReq);
- pReq = NULL;
-}
-
-
-/**
- * Waits for a guest thread's event to get triggered.
- *
- * @return IPRT status code.
- * @param pReq Request to wait for.
- */
-int VBoxServiceControlThreadRequestWait(PVBOXSERVICECTRLREQUEST pReq)
-{
- AssertPtrReturn(pReq, VERR_INVALID_POINTER);
-
- /* Wait on the request to get completed (or we are asked to abort/shutdown). */
- int rc = RTSemEventMultiWait(pReq->Event, RT_INDEFINITE_WAIT);
- if (RT_SUCCESS(rc))
- {
- VBoxServiceVerbose(4, "Performed request with rc=%Rrc, cbData=%u\n",
- pReq->rc, pReq->cbData);
-
- /* Give back overall request result. */
- rc = pReq->rc;
- }
- else
- VBoxServiceError("Waiting for request failed, rc=%Rrc\n", rc);
-
- return rc;
-}
-
-
-/**
- * Sets up the redirection / pipe / nothing for one of the standard handles.
- *
- * @returns IPRT status code. No client replies made.
- * @param pszHowTo How to set up this standard handle.
- * @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.
- */
-static int VBoxServiceControlThreadSetupPipe(const char *pszHowTo, int fd,
- PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
-{
- AssertPtrReturn(ph, VERR_INVALID_POINTER);
- AssertPtrReturn(pph, VERR_INVALID_POINTER);
- AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
-
- int rc;
-
- ph->enmType = RTHANDLETYPE_PIPE;
- ph->u.hPipe = NIL_RTPIPE;
- *pph = NULL;
- *phPipe = NIL_RTPIPE;
-
- if (!strcmp(pszHowTo, "|"))
- {
- /*
- * 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;
- }
- else if (!strcmp(pszHowTo, "/dev/null"))
- {
- /*
- * Redirect to/from /dev/null.
- */
- RTFILE hFile;
- rc = RTFileOpenBitBucket(&hFile, fd == 0 ? RTFILE_O_READ : RTFILE_O_WRITE);
- if (RT_FAILURE(rc))
- return rc;
-
- ph->enmType = RTHANDLETYPE_FILE;
- ph->u.hFile = hFile;
- *pph = ph;
- }
- else /* Add other piping stuff here. */
- rc = VINF_SUCCESS; /* Same as parent (us). */
-
- 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 VBoxServiceControlThreadMakeFullPath(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, "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 if
- * VBOXSERVICE_NAME is specified as pszFileName.
- *
- * @return IPRT status code.
- * @param pszFileName File name to resolve.
- * @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 VBoxServiceControlThreadResolveExecutable(const char *pszFileName,
- char *pszResolved, size_t cbResolved)
-{
- AssertPtrReturn(pszFileName, VERR_INVALID_POINTER);
- AssertPtrReturn(pszResolved, VERR_INVALID_POINTER);
- AssertReturn(cbResolved, VERR_INVALID_PARAMETER);
-
- int rc = VINF_SUCCESS;
-
- char szPathToResolve[RTPATH_MAX];
- if ( (g_pszProgName && (RTStrICmp(pszFileName, g_pszProgName) == 0))
- || !RTStrICmp(pszFileName, VBOXSERVICE_NAME))
- {
- /* Resolve executable name of this process. */
- if (!RTProcGetExecutablePath(szPathToResolve, sizeof(szPathToResolve)))
- rc = VERR_FILE_NOT_FOUND;
- }
- else
- {
- /* Take the raw argument to resolve. */
- rc = RTStrCopy(szPathToResolve, sizeof(szPathToResolve), pszFileName);
- }
-
- if (RT_SUCCESS(rc))
- {
- rc = VBoxServiceControlThreadMakeFullPath(szPathToResolve, pszResolved, cbResolved);
- if (RT_SUCCESS(rc))
- VBoxServiceVerbose(3, "Looked up executable: %s -> %s\n",
- pszFileName, pszResolved);
- }
-
- if (RT_FAILURE(rc))
- VBoxServiceError("Failed to lookup executable \"%s\" with rc=%Rrc\n",
- pszFileName, rc);
- 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. Optional.
- * @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 VBoxServiceControlThreadAllocateArgv(const char *pszArgv0,
- const char * const *papszArgs,
- bool fExpandArgs, char ***ppapszArgv)
-{
- AssertPtrReturn(ppapszArgv, VERR_INVALID_POINTER);
-
- VBoxServiceVerbose(3, "VBoxServiceControlThreadPrepareArgv: pszArgv0=%p, papszArgs=%p, fExpandArgs=%RTbool, ppapszArgv=%p\n",
- pszArgv0, papszArgs, fExpandArgs, ppapszArgv);
-
- int rc = VINF_SUCCESS;
- uint32_t cArgs;
- for (cArgs = 0; papszArgs[cArgs]; cArgs++)
- {
- if (cArgs >= UINT32_MAX - 2)
- return VERR_BUFFER_OVERFLOW;
- }
-
- /* Allocate new argv vector (adding + 2 for argv0 + termination). */
- size_t cbSize = (cArgs + 2) * sizeof(char*);
- char **papszNewArgv = (char**)RTMemAlloc(cbSize);
- if (!papszNewArgv)
- return VERR_NO_MEMORY;
-
-#ifdef DEBUG
- VBoxServiceVerbose(3, "VBoxServiceControlThreadPrepareArgv: cbSize=%RU32, cArgs=%RU32\n",
- cbSize, cArgs);
-#endif
-
- size_t i = 0; /* Keep the argument counter in scope for cleaning up on failure. */
-
- rc = RTStrDupEx(&papszNewArgv[0], pszArgv0);
- if (RT_SUCCESS(rc))
- {
- for (; i < cArgs; i++)
- {
- char *pszArg;
-#if 0 /* Arguments expansion -- untested. */
- if (fExpandArgs)
- {
- /* According to MSDN the limit on older Windows version is 32K, whereas
- * Vista+ there are no limits anymore. We still stick to 4K. */
- char szExpanded[_4K];
-# ifdef RT_OS_WINDOWS
- if (!ExpandEnvironmentStrings(papszArgs[i], szExpanded, sizeof(szExpanded)))
- rc = RTErrConvertFromWin32(GetLastError());
-# else
- /* No expansion for non-Windows yet. */
- rc = RTStrCopy(papszArgs[i], sizeof(szExpanded), szExpanded);
-# endif
- if (RT_SUCCESS(rc))
- rc = RTStrDupEx(&pszArg, szExpanded);
- }
- else
-#endif
- rc = RTStrDupEx(&pszArg, papszArgs[i]);
-
- if (RT_FAILURE(rc))
- break;
-
- papszNewArgv[i + 1] = pszArg;
- }
-
- if (RT_SUCCESS(rc))
- {
- /* Terminate array. */
- papszNewArgv[cArgs + 1] = NULL;
-
- *ppapszArgv = papszNewArgv;
- }
- }
-
- if (RT_FAILURE(rc))
- {
- for (i; i > 0; i--)
- RTStrFree(papszNewArgv[i]);
- RTMemFree(papszNewArgv);
- }
-
- return rc;
-}
-
-
-void VBoxServiceControlThreadFreeArgv(char **papszArgv)
-{
- if (papszArgv)
- {
- size_t i = 0;
- while (papszArgv[i])
- RTStrFree(papszArgv[i++]);
- RTMemFree(papszArgv);
- }
-}
-
-
-/**
- * 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 VBoxServiceControlThreadCreateProcess(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];
-
- /* Do we need to expand environment variables in arguments? */
- bool fExpandArgs = (fFlags & EXECUTEPROCESSFLAG_EXPAND_ARGUMENTS) ? true : false;
-
-#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"))
- {
- /* 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 = VBoxServiceControlThreadAllocateArgv(szSysprepCmd /* argv0 */, papszArgs,
- fExpandArgs, &papszArgsExp);
- if (RT_SUCCESS(rc))
- {
- rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */,
- phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
- NULL /* pszPassword */, phProcess);
- VBoxServiceControlThreadFreeArgv(papszArgsExp);
- }
- }
-
- if (RT_FAILURE(rc))
- VBoxServiceVerbose(3, "Starting sysprep returned rc=%Rrc\n", rc);
-
- 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 = VBoxServiceControlThreadResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp));
- }
- else
- {
-#endif
- /*
- * Do the environment variables expansion on executable and arguments.
- */
- rc = VBoxServiceControlThreadResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
-#ifdef VBOXSERVICE_TOOLBOX
- }
-#endif
- if (RT_SUCCESS(rc))
- {
- char **papszArgsExp;
- rc = VBoxServiceControlThreadAllocateArgv(pszExec /* Always use the unmodified executable name as argv0. */,
- papszArgs /* Append the rest of the argument vector (if any). */,
- fExpandArgs, &papszArgsExp);
- if (RT_FAILURE(rc))
- {
- /* Don't print any arguments -- may contain passwords or other sensible data! */
- VBoxServiceError("Could not prepare arguments, rc=%Rrc\n", rc);
- }
- else
- {
- uint32_t uProcFlags = 0;
- if (fFlags)
- {
- if (fFlags & EXECUTEPROCESSFLAG_HIDDEN)
- uProcFlags |= RTPROC_FLAGS_HIDDEN;
- if (fFlags & EXECUTEPROCESSFLAG_NO_PROFILE)
- 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, "Command: %s\n", szExecExp);
- for (size_t i = 0; papszArgsExp[i]; i++)
- VBoxServiceVerbose(3, "\targv[%ld]: %s\n", i, papszArgsExp[i]);
-#endif
- VBoxServiceVerbose(3, "Starting process \"%s\" ...\n", szExecExp);
-
- /* Do normal execution. */
- rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags,
- phStdIn, phStdOut, phStdErr,
- *pszAsUser ? pszAsUser : NULL,
- *pszPassword ? pszPassword : NULL,
- phProcess);
-
- VBoxServiceVerbose(3, "Starting process \"%s\" returned rc=%Rrc\n",
- szExecExp, rc);
-
- VBoxServiceControlThreadFreeArgv(papszArgsExp);
- }
- }
- return rc;
-}
-
-/**
- * The actual worker routine (loop) for a started guest process.
- *
- * @return IPRT status code.
- * @param PVBOXSERVICECTRLTHREAD Thread data associated with a started process.
- */
-static int VBoxServiceControlThreadProcessWorker(PVBOXSERVICECTRLTHREAD pThread)
-{
- AssertPtrReturn(pThread, VERR_INVALID_POINTER);
- VBoxServiceVerbose(3, "Thread of process pThread=0x%p = \"%s\" started\n",
- pThread, pThread->pszCmd);
-
- int rc = VBoxServiceControlListSet(VBOXSERVICECTRLTHREADLIST_RUNNING, pThread);
- AssertRC(rc);
-
- rc = VbglR3GuestCtrlConnect(&pThread->uClientID);
- if (RT_FAILURE(rc))
- {
- VBoxServiceError("Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
- RTThreadUserSignal(RTThreadSelf());
- return rc;
- }
- VBoxServiceVerbose(3, "Guest process \"%s\" got client ID=%u, flags=0x%x\n",
- pThread->pszCmd, pThread->uClientID, pThread->uFlags);
-
- 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 = VBoxServiceControlThreadSetupPipe("|", 0 /*STDIN_FILENO*/,
- &hStdIn, &phStdIn, &pThread->pipeStdInW);
- if (RT_SUCCESS(rc))
- {
- RTHANDLE hStdOut;
- PRTHANDLE phStdOut;
- RTPIPE pipeStdOutR;
- rc = VBoxServiceControlThreadSetupPipe( (pThread->uFlags & EXECUTEPROCESSFLAG_WAIT_STDOUT)
- ? "|" : "/dev/null",
- 1 /*STDOUT_FILENO*/,
- &hStdOut, &phStdOut, &pipeStdOutR);
- if (RT_SUCCESS(rc))
- {
- RTHANDLE hStdErr;
- PRTHANDLE phStdErr;
- RTPIPE pipeStdErrR;
- rc = VBoxServiceControlThreadSetupPipe( (pThread->uFlags & EXECUTEPROCESSFLAG_WAIT_STDERR)
- ? "|" : "/dev/null",
- 2 /*STDERR_FILENO*/,
- &hStdErr, &phStdErr, &pipeStdErrR);
- 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))
- {
- uint32_t uFlags = RTPOLL_EVT_ERROR;
-#if 0
- /* Add reading event to pollset to get some more information. */
- uFlags |= RTPOLL_EVT_READ;
-#endif
- /* Stdin. */
- if (RT_SUCCESS(rc))
- rc = RTPollSetAddPipe(hPollSet, pThread->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN);
- /* Stdout. */
- if (RT_SUCCESS(rc))
- rc = RTPollSetAddPipe(hPollSet, pipeStdOutR, uFlags, VBOXSERVICECTRLPIPEID_STDOUT);
- /* Stderr. */
- if (RT_SUCCESS(rc))
- rc = RTPollSetAddPipe(hPollSet, pipeStdErrR, uFlags, 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 = VBoxServiceControlThreadCreateProcess(pThread->pszCmd, pThread->papszArgs, hEnv, pThread->uFlags,
- phStdIn, phStdOut, phStdErr,
- pThread->pszUser, pThread->pszPassword,
- &hProcess);
- if (RT_FAILURE(rc))
- VBoxServiceError("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 @bugref{5068}).
- */
- int rc2 = RTThreadUserSignal(RTThreadSelf());
- if (RT_SUCCESS(rc))
- 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 = VBoxServiceControlThreadProcLoop(pThread,
- hProcess, pThread->uTimeLimitMS, hPollSet,
- &pThread->pipeStdInW, &pipeStdOutR, &pipeStdErrR);
-
- /*
- * 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)))
- pipeStdErrR = NIL_RTPIPE;
- if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDOUT, NULL)))
- pipeStdOutR = NIL_RTPIPE;
- if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN, NULL)))
- pThread->pipeStdInW = NIL_RTPIPE;
- }
- }
- RTPollSetDestroy(hPollSet);
-
- RTPipeClose(pThread->hNotificationPipeR);
- pThread->hNotificationPipeR = NIL_RTPIPE;
- RTPipeClose(pThread->hNotificationPipeW);
- pThread->hNotificationPipeW = NIL_RTPIPE;
- }
- RTPipeClose(pipeStdErrR);
- pipeStdErrR = NIL_RTPIPE;
- RTHandleClose(phStdErr);
- if (phStdErr)
- RTHandleClose(phStdErr);
- }
- RTPipeClose(pipeStdOutR);
- pipeStdOutR = NIL_RTPIPE;
- RTHandleClose(&hStdOut);
- if (phStdOut)
- RTHandleClose(phStdOut);
- }
- RTPipeClose(pThread->pipeStdInW);
- pThread->pipeStdInW = NIL_RTPIPE;
- RTHandleClose(phStdIn);
- }
- }
- RTEnvDestroy(hEnv);
- }
-
- /* Move thread to stopped thread list. */
- int rc2 = VBoxServiceControlListSet(VBOXSERVICECTRLTHREADLIST_STOPPED, pThread);
- AssertRC(rc2);
-
- if (pThread->uClientID)
- {
- if (RT_FAILURE(rc))
- {
- rc2 = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, pThread->uPID,
- PROC_STS_ERROR, rc,
- NULL /* pvData */, 0 /* cbData */);
- if (RT_FAILURE(rc2))
- VBoxServiceError("Could not report process failure error; rc=%Rrc (process error %Rrc)\n",
- rc2, rc);
- }
-
- VBoxServiceVerbose(3, "[PID %u]: Cancelling pending host requests (client ID=%u)\n",
- pThread->uPID, pThread->uClientID);
- rc2 = VbglR3GuestCtrlCancelPendingWaits(pThread->uClientID);
- if (RT_FAILURE(rc2))
- {
- VBoxServiceError("[PID %u]: Cancelling pending host requests failed; rc=%Rrc\n",
- pThread->uPID, rc2);
- if (RT_SUCCESS(rc))
- rc = rc2;
- }
-
- /* Disconnect from guest control service. */
- VBoxServiceVerbose(3, "[PID %u]: Disconnecting (client ID=%u) ...\n",
- pThread->uPID, pThread->uClientID);
- VbglR3GuestCtrlDisconnect(pThread->uClientID);
- pThread->uClientID = 0;
- }
-
- VBoxServiceVerbose(3, "[PID %u]: Thread of process \"%s\" ended with rc=%Rrc\n",
- pThread->uPID, pThread->pszCmd, rc);
-
- /* Update started/stopped status. */
- ASMAtomicXchgBool(&pThread->fStopped, true);
- ASMAtomicXchgBool(&pThread->fStarted, false);
-
- /*
- * 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) VBoxServiceControlThread(RTTHREAD ThreadSelf, void *pvUser)
-{
- PVBOXSERVICECTRLTHREAD pThread = (VBOXSERVICECTRLTHREAD*)pvUser;
- AssertPtrReturn(pThread, VERR_INVALID_POINTER);
- return VBoxServiceControlThreadProcessWorker(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 uContextID Context ID to associate the process to start with.
- * @param pProcess Process info.
- */
-int VBoxServiceControlThreadStart(uint32_t uContextID,
- PVBOXSERVICECTRLPROCESS pProcess)
-{
- AssertPtrReturn(pProcess, 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 = gstsvcCntlExecThreadInit(pThread, pProcess, uContextID);
- if (RT_SUCCESS(rc))
- {
- static uint32_t s_uCtrlExecThread = 0;
- if (s_uCtrlExecThread++ == UINT32_MAX)
- s_uCtrlExecThread = 0; /* Wrap around to not let IPRT freak out. */
- rc = RTThreadCreateF(&pThread->Thread, VBoxServiceControlThread,
- pThread /*pvUser*/, 0 /*cbStack*/,
- RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "gctl%u", s_uCtrlExecThread);
- if (RT_FAILURE(rc))
- {
- VBoxServiceError("RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n",
- rc, pThread);
- }
- else
- {
- VBoxServiceVerbose(4, "Waiting for thread to initialize ...\n");
-
- /* Wait for the thread to initialize. */
- rc = RTThreadUserWait(pThread->Thread, 60 * 1000 /* 60 seconds max. */);
- AssertRC(rc);
- if ( ASMAtomicReadBool(&pThread->fShutdown)
- || RT_FAILURE(rc))
- {
- VBoxServiceError("Thread for process \"%s\" failed to start, rc=%Rrc\n",
- pProcess->szCmd, rc);
- }
- else
- {
- ASMAtomicXchgBool(&pThread->fStarted, true);
- }
- }
- }
-
- if (RT_FAILURE(rc))
- RTMemFree(pThread);
-
- return rc;
-}
-
-
-/**
- * Performs a request to a specific (formerly started) guest process and waits
- * for its response.
- *
- * @return IPRT status code.
- * @param uPID PID of guest process to perform a request to.
- * @param pRequest Pointer to request to perform.
- */
-int VBoxServiceControlThreadPerform(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). */
-
- int rc = VINF_SUCCESS;
- PVBOXSERVICECTRLTHREAD pThread = VBoxServiceControlLockThread(uPID);
- if (pThread)
- {
- if (ASMAtomicReadBool(&pThread->fShutdown))
- {
- rc = VERR_CANCELLED;
- }
- else
- {
- /* Set request structure pointer. */
- pThread->pRequest = pRequest;
-
- /** @todo To speed up simultaneous guest process handling we could add a worker threads
- * or queue 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. */
- Assert(pThread->hNotificationPipeW != NIL_RTPIPE);
- size_t cbWritten;
- if (RT_SUCCESS(rc))
- rc = RTPipeWrite(pThread->hNotificationPipeW, "i", 1, &cbWritten);
-
- if ( RT_SUCCESS(rc)
- && cbWritten)
- {
- VBoxServiceVerbose(3, "[PID %u]: Waiting for response on enmType=%u, pvData=0x%p, cbData=%u\n",
- uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData);
-
- rc = VBoxServiceControlThreadRequestWait(pRequest);
- }
- }
-
- VBoxServiceControlUnlockThread(pThread);
- }
- else /* PID not found! */
- rc = VERR_NOT_FOUND;
-
- VBoxServiceVerbose(3, "[PID %u]: Performed enmType=%u, uCID=%u, pvData=0x%p, cbData=%u, rc=%Rrc\n",
- uPID, pRequest->enmType, pRequest->uCID, pRequest->pvData, pRequest->cbData, rc);
- return rc;
-}
-