diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-03-26 19:21:20 +0000 |
---|---|---|
committer | <> | 2014-05-08 15:03:54 +0000 |
commit | fb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch) | |
tree | c2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp | |
parent | 58ed4748338f9466599adfc8a9171280ed99e23f (diff) | |
download | VirtualBox-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.cpp | 1794 |
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; -} - |