GuestCtrlImpl.cpp revision f811ed461f65317219f3e66f4ceaa44dfd8a3b4b
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * VirtualBox COM class implementation: Guest
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * Copyright (C) 2006-2011 Oracle Corporation
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * This file is part of VirtualBox Open Source Edition (OSE), as
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * available from http://www.virtualbox.org. This file is free software;
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * you can redistribute it and/or modify it under the terms of the GNU
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * General Public License (GPL) as published by the Free Software
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * Foundation, in version 2 as it comes in the "COPYING" file of the
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor// public methods only for internal purposes
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor/////////////////////////////////////////////////////////////////////////////
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * Appends environment variables to the environment block.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * Each var=value pair is separated by the null character ('\\0'). The whole
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * block will be stored in one blob and disassembled on the guest side later to
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * fit into the HGCM param structure.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * @returns VBox status code.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * @param pszEnvVar The environment variable=value to append to the
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * environment block.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * @param ppvList This is actually a pointer to a char pointer
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * variable which keeps track of the environment block
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * that we're constructing.
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @param pcbList Pointer to the variable holding the current size of
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * the environment block. (List is a misnomer, go
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * ahead a be confused.)
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @param pcEnvVars Pointer to the variable holding count of variables
3016c72ae8173bcfc0989ff1b297d4f27f445108lgentis * stored in the environment block.
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzorint Guest::prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnvVars)
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor uint32_t cchEnv = strlen(pszEnv); Assert(cchEnv >= 2);
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor uint32_t cbNewLen = *pcbList + cchEnv + 1; /* Include zero termination. */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor char *pvTmp = (char *)RTMemRealloc(*ppvList, cbNewLen);
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor /* Reset counters. */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor *pcbList += cchEnv + 1; /* Include zero termination. */
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * Adds a callback with a user provided data block and an optional progress object
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * to the callback map. A callback is identified by a unique context ID which is used
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * to identify a callback from the guest side.
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @return IPRT status code.
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @param pCallback
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @param puContextID
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzorint Guest::callbackAdd(const PVBOXGUESTCTRL_CALLBACK pCallback, uint32_t *puContextID)
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor /* puContextID is optional. */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor /* Create a new context ID and assign it. */
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor /* Create a new context ID ... */
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor /* Is the context ID already used? Try next ID ... */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor /* Callback with context ID was not found. This means
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * we can use this context ID for our new callback we want
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * to add below. */
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor /* Add callback with new context ID to our callback map. */
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor /* Report back new context ID. */
3670f762ba6e3c38c29ea8a90fcdd5281ab13d53lgentis * Does not do locking!
3670f762ba6e3c38c29ea8a90fcdd5281ab13d53lgentis * @param uContextID
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor LogFlowFunc(("Destroying callback with CID=%u ...\n", uContextID));
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor /* Notify callback (if necessary). */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor int rc = callbackNotifyEx(uContextID, VERR_CANCELLED,
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor Guest::tr("VM is shutting down, canceling uncompleted guest requests ..."));
66a40356a2baa1bdc3f91e91399a8bf3d2dbe7c6lgentis /* Remove callback context (not used anymore). */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzorint Guest::callbackGetUserData(uint32_t uContextID, eVBoxGuestCtrlCallbackType *pEnmType,
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor /* pEnmType is optional. */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor /* pcbData is optional. */
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor CallbackMapIterConst it = mCallbackMap.find(uContextID);
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor memcpy(pvData, it->second.pvData, it->second.cbData);
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor/* Does not do locking! Caller has to take care of it because the caller needs to
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * modify the data ...*/
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzorvoid* Guest::callbackGetUserDataMutableRaw(uint32_t uContextID, size_t *pcbData)
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor /* pcbData is optional. */
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor CallbackMapIterConst it = mCallbackMap.find(uContextID);
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzorint Guest::callbackInit(PVBOXGUESTCTRL_CALLBACK pCallback, eVBoxGuestCtrlCallbackType enmType,
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor /* Everything else is optional. */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor PCALLBACKDATAEXECOUT pData = (PCALLBACKDATAEXECOUT)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECINSTATUS));
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor pCallback->cbData = sizeof(PCALLBACKDATAEXECINSTATUS);
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor /* Init/set common stuff. */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor CallbackMapIterConst it = mCallbackMap.find(uContextID);
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor HRESULT hRC = pProgress->COMGETTER(Canceled)(&fCanceled);
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor return false;
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor return true; /* No progress / error means canceled. */
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor CallbackMapIterConst it = mCallbackMap.find(uContextID);
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor HRESULT hRC = pProgress->COMGETTER(Completed)(&fCompleted);
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor return true;
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor return false;
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzorint Guest::callbackMoveForward(uint32_t uContextID, const char *pszMessage)
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor CallbackMapIterConst it = mCallbackMap.find(uContextID);
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor HRESULT hr = pProgress->SetNextOperation(Bstr(pszMessage).raw(), 1 /* Weight */);
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * @return IPRT status code.
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @param uContextID
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @param iRC
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @param pszMessage
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzorint Guest::callbackNotifyEx(uint32_t uContextID, int iRC, const char *pszMessage)
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor LogFlowFunc(("Notifying callback with CID=%u, iRC=%d, pszMsg=%s ...\n",
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor CallbackMapIterConst it = mCallbackMap.find(uContextID);
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor HRESULT hRC = pProgress->COMGETTER(Canceled)(&fCanceled);
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor /* If progress already canceled do nothing here. */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * Assume we didn't complete to make sure we clean up even if the
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * following call fails.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor HRESULT hRC = pProgress->COMGETTER(Completed)(&fCompleted);
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * cancel won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * is disconnecting without having the chance to sending a status message before, so we
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * have to abort here to make sure the host never hangs/gets stuck while waiting for the
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * progress object to become signalled.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor hRC = pProgress->notifyComplete(VBOX_E_IPRT_ERROR /* Must not be S_OK. */,
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * Do *not* NULL pProgress here, because waiting function like executeProcess()
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * will still rely on this object for checking whether they have to give up!
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor /* If pProgress is not found (anymore) that's fine.
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * Might be destroyed already. */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @return IPRT status code.
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @param uPID
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @param iRC
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @param pszMessage
3670f762ba6e3c38c29ea8a90fcdd5281ab13d53lgentisint Guest::callbackNotifyAllForPID(uint32_t uPID, int iRC, const char *pszMessage)
3670f762ba6e3c38c29ea8a90fcdd5281ab13d53lgentis for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
3670f762ba6e3c38c29ea8a90fcdd5281ab13d53lgentis /* When waiting for process output while the process is destroyed,
04180f61123bcb92117fb5285f6143f196180f20lgentis * make sure we also destroy the actual waiting operation (internal progress object)
3670f762ba6e3c38c29ea8a90fcdd5281ab13d53lgentis * in order to not block the caller. */
fb25b82560b7fcaffa006cb4738d86acc561b6f4lgentis PCALLBACKDATAEXECOUT pItData = (PCALLBACKDATAEXECOUT)it->second.pvData;
3670f762ba6e3c38c29ea8a90fcdd5281ab13d53lgentis /* When waiting for injecting process input while the process is destroyed,
3670f762ba6e3c38c29ea8a90fcdd5281ab13d53lgentis * make sure we also destroy the actual waiting operation (internal progress object)
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * in order to not block the caller. */
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor PCALLBACKDATAEXECINSTATUS pItData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
8f7ac4f16af1eba020eaa76d3a7504f07aed9446lgentis AssertMsgFailed(("Unknown callback type %d, iRC=%d, message=%s\n",
8f7ac4f16af1eba020eaa76d3a7504f07aed9446lgentis it->second.mType, iRC, pszMessage ? pszMessage : "<No message given>"));
8f7ac4f16af1eba020eaa76d3a7504f07aed9446lgentisint Guest::callbackNotifyComplete(uint32_t uContextID)
8f7ac4f16af1eba020eaa76d3a7504f07aed9446lgentis return callbackNotifyEx(uContextID, S_OK, NULL /* No message */);
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * Waits for a callback (using its context ID) to complete.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * @return IPRT status code.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * @param uContextID Context ID to wait for.
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * @param lStage Stage to wait for. Specify -1 if no staging is present/required.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * Specifying a stage is only needed if there's a multi operation progress
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * object to wait for.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor * @param lTimeout Timeout (in ms) to wait for.
8f7ac4f16af1eba020eaa76d3a7504f07aed9446lgentisint Guest::callbackWaitForCompletion(uint32_t uContextID, LONG lStage, LONG lTimeout)
8f7ac4f16af1eba020eaa76d3a7504f07aed9446lgentis * Wait for the HGCM low level callback until the process
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * has been started (or something went wrong). This is necessary to
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor * get the PID.
fed038ccf8597b6a19d85985d115fe96b8d0de03gryzor CallbackMapIterConst it = mCallbackMap.find(uContextID);
bbcf0849a87b48d6e116437100a2fbd78c8124dbgryzor rc = pProgress->WaitForOperationCompletion((ULONG)lStage, lTimeout);
return vrc;
void *pvParms,
using namespace guestControl;
#ifdef DEBUG_andy
switch (u32Function)
case GUEST_DISCONNECTED:
PCALLBACKDATACLIENTDISCONNECTED pCBData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvParms);
AssertReturn(CALLBACKDATAMAGIC_CLIENT_DISCONNECTED == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
case GUEST_EXEC_SEND_STATUS:
case GUEST_EXEC_SEND_OUTPUT:
return rc;
/* Function for handling the execution start/termination notification. */
if (pCallbackData)
if (!fCbCanceled)
case PROC_STS_STARTED:
case PROC_STS_TOK:
LogRel(("Guest process (PID %u) timed out and was killed\n", pData->u32PID)); /** @todo Add process name */
case PROC_STS_TOA:
LogRel(("Guest process (PID %u) timed out and could not be killed\n", pData->u32PID)); /** @todo Add process name */
case PROC_STS_DWN:
LogRel(("Guest process (PID %u) killed because system is shutting down\n", pData->u32PID)); /** @todo Add process name */
* even if the executed process was killed because the system/VBoxService is shutting down.
case PROC_STS_ERROR:
return vrc;
if (pCallbackData)
AssertReleaseMsgFailed(("Process output status (PID=%u, CID=%u) does not have allocated callback data!\n",
int vrc;
return vrc;
if (pCallbackData)
AssertReleaseMsgFailed(("Process input status (PID=%u, CID=%u) does not have allocated callback data!\n",
return VINF_SUCCESS;
return VERR_ALREADY_EXISTS;
return VINF_SUCCESS;
return VERR_NOT_FOUND;
int Guest::processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags)
return VINF_SUCCESS;
return VERR_NOT_FOUND;
return hRC;
return hRC;
#ifndef VBOX_WITH_GUEST_CONTROL
using namespace guestControl;
#ifdef VBOX_WITH_GUEST_CONTROL
if (!fCompleted)
if (fCompleted)
if (aProgress)
if (aPID)
else if (fCanceled)
return rc;
case PROC_STS_STARTED:
case PROC_STS_TOK:
case PROC_STS_TOA:
case PROC_STS_DWN:
case PROC_STS_ERROR:
return rc;
return rc;
return rc;
return rc;
return hr;
if (uTimeoutMS == 0)
return hRC;
if (pRetStatus)
if (puRetExitCode)
return hRC;
using namespace guestControl;
if (pRC)
TRUE,
if (aTimeoutMS == 0)
if (aArguments)
if (uNumArgs > 0)
if (aEnvironment)
if (pVMMDev)
i, paParms);
for (unsigned i = 0; i < uNumArgs; i++)
if (pRC)
return rc;
STDMETHODIMP Guest::SetProcessInput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, ComSafeArrayIn(BYTE, aData), ULONG *aBytesWritten)
#ifndef VBOX_WITH_GUEST_CONTROL
using namespace guestControl;
if (aFlags)
if (aTimeoutMS == 0)
if (pVMMDev)
i, paParms);
case INPUT_STS_WRITTEN:
return rc;
STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
#ifndef VBOX_WITH_GUEST_CONTROL
using namespace guestControl;
if (aSize < 0)
if (aSize == 0)
if (aFlags)
if (aTimeoutMS == 0)
if (pVMMDev)
i, paParms);
return rc;
STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ExecuteProcessStatus_T *aStatus)
#ifndef VBOX_WITH_GUEST_CONTROL
return rc;
#ifndef VBOX_WITH_GUEST_CONTROL
return rc;
#ifndef VBOX_WITH_GUEST_CONTROL
return rc;
#ifndef VBOX_WITH_GUEST_CONTROL
if (aFlags)
return rc;