GuestCtrlPrivate.cpp revision 81dfa298981df637a707b142ebd03cb7d3385097
/* $Id$ */
/** @file
*
* Internal helpers/structures for guest control functionality.
*/
/*
* Copyright (C) 2011-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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 "GuestCtrlImplPrivate.h"
#include "GuestSessionImpl.h"
#include "VMMDev.h"
#ifdef DEBUG
#endif /* DEBUG */
#ifdef LOG_GROUP
#endif
#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
/******************************************************************************
* Structures and Typedefs *
******************************************************************************/
{
/* Rest is optional. */
int rc = VINF_SUCCESS;
if (cEnv)
{
{
char *pszEnv;
{
rc = VERR_NO_MEMORY;
break;
}
}
}
if (pcbEnv)
if (pcEnvVars)
return rc;
}
void GuestEnvironment::Clear(void)
{
}
{
int rc = VINF_SUCCESS;
++it)
{
}
return rc;
}
{
size_t s = 0;
++it, ++s)
{
}
return VINF_SUCCESS;
}
/* static */
{
if (pvEnv)
}
{
return Utf8Str("");
}
{
return strRet;
}
{
}
{
/** @todo Do some validation using regex. */
return VERR_INVALID_PARAMETER;
int rc = VINF_SUCCESS;
{
if ( !RT_C_IS_ALNUM(*pszString)
&& !RT_C_IS_GRAPH(*pszString))
*pszString++;
}
if (RT_SUCCESS(rc))
return rc;
}
{
/* Skip completely empty pairs. Note that we still need pairs with a valid
* (set) key and an empty value. */
return VINF_SUCCESS;
int rc = VINF_SUCCESS;
size_t p = 0;
{
{
break;
}
#ifdef DEBUG
LogFlowFunc(("strKey=%s, strValue=%s\n",
#endif
}
return rc;
}
{
return mEnvironment.size();
}
{
{
return VINF_SUCCESS;
}
return VERR_NOT_FOUND;
}
{
return *this;
}
{
++it)
{
}
return *this;
}
/**
* Appends environment variables to the environment block.
*
* Each var=value pair is separated by the null character ('\\0'). The whole
* block will be stored in one blob and disassembled on the guest side later to
* fit into the HGCM param structure.
*
* @returns VBox status code.
*
* @param pszEnvVar The environment variable=value to append to the
* environment block.
* @param ppvList This is actually a pointer to a char pointer
* variable which keeps track of the environment block
* that we're constructing.
* @param pcbList Pointer to the variable holding the current size of
* the environment block. (List is a misnomer, go
* ahead a be confused.)
* @param pcEnvVars Pointer to the variable holding count of variables
* stored in the environment block.
*/
int GuestEnvironment::appendToEnvBlock(const char *pszEnv, void **ppvList, size_t *pcbList, uint32_t *pcEnvVars)
{
int rc = VINF_SUCCESS;
if (*ppvList)
{
rc = VERR_NO_MEMORY;
else
{
}
}
else
{
char *pszTmp;
{
/* Reset counters. */
*pcEnvVars = 0;
*pcbList = 0;
}
}
if (RT_SUCCESS(rc))
{
}
return rc;
}
{
LogFlowFunc(("\n"));
int rc = VINF_SUCCESS;
try
{
#ifdef DEBUG
#endif
/* Object name. */
/* Type. */
/** @todo Add more types! */
else
/* Object size. */
/** @todo Add complete ls info! */
}
catch (int rc2)
{
}
return rc;
}
{
LogFlowFunc(("\n"));
int rc;
try
{
#ifdef DEBUG
#endif
/* Object name. */
/* Assign the stream block's rc. */
}
catch (int rc2)
{
}
return rc;
}
{
LogFlowFunc(("\n"));
int rc = VINF_SUCCESS;
try
{
#ifdef DEBUG
#endif
/* Node ID, optional because we don't include this
* in older VBoxService (< 4.2) versions. */
/* Object name. */
/* Type. */
/** @todo Add more types! */
else
/* Object size. */
/** @todo Add complete stat info! */
}
catch (int rc2)
{
}
return rc;
}
///////////////////////////////////////////////////////////////////////////////
/** @todo *NOT* thread safe yet! */
/** @todo Add exception handling for STL stuff! */
{
}
/*
GuestProcessStreamBlock::GuestProcessStreamBlock(const GuestProcessStreamBlock &otherBlock)
{
for (GuestCtrlStreamPairsIter it = otherBlock.mPairs.begin();
it != otherBlock.end(); it++)
{
mPairs[it->first] = new
if (it->second.pszValue)
{
RTMemFree(it->second.pszValue);
it->second.pszValue = NULL;
}
}
}*/
{
Clear();
}
/**
* Destroys the currently stored stream pairs.
*
* @return IPRT status code.
*/
void GuestProcessStreamBlock::Clear(void)
{
}
#ifdef DEBUG
void GuestProcessStreamBlock::DumpToLog(void) const
{
LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n",
{
}
}
#endif
/**
* Returns a 64-bit signed integer of a specified key.
*
* @return IPRT status code. VERR_NOT_FOUND if key was not found.
* @param pszKey Name of key to get the value for.
* @param piVal Pointer to value to return.
*/
{
if (pszValue)
{
return VINF_SUCCESS;
}
return VERR_NOT_FOUND;
}
/**
* Returns a 64-bit integer of a specified key.
*
* @return int64_t Value to return, 0 if not found / on failure.
* @param pszKey Name of key to get the value for.
*/
{
return iVal;
return 0;
}
/**
* Returns the current number of stream pairs.
*
* @return uint32_t Current number of stream pairs.
*/
{
}
/**
* Gets the return code (name = "rc") of this stream block.
*
* @return IPRT status code.
*/
int GuestProcessStreamBlock::GetRc(void) const
{
if (pszValue)
{
return RTStrToInt16(pszValue);
}
return VERR_NOT_FOUND;
}
/**
* Returns a string value of a specified key.
*
* @return uint32_t Pointer to string to return, NULL if not found / on failure.
* @param pszKey Name of key to get the value for.
*/
{
try
{
}
{
}
return NULL;
}
/**
* Returns a 32-bit unsigned integer of a specified key.
*
* @return IPRT status code. VERR_NOT_FOUND if key was not found.
* @param pszKey Name of key to get the value for.
* @param puVal Pointer to value to return.
*/
{
if (pszValue)
{
return VINF_SUCCESS;
}
return VERR_NOT_FOUND;
}
/**
* Returns a 32-bit unsigned integer of a specified key.
*
* @return uint32_t Value to return, 0 if not found / on failure.
* @param pszKey Name of key to get the value for.
*/
{
return uVal;
return 0;
}
/**
* Sets a value to a key or deletes a key by setting a NULL value.
*
* @return IPRT status code.
* @param pszKey Key name to process.
* @param pszValue Value to set. Set NULL for deleting the key.
*/
{
int rc = VINF_SUCCESS;
try
{
/* Take a shortcut and prevent crashes on some funny versions
* of STL if map is empty initially. */
{
}
if (pszValue)
{
}
}
{
}
return rc;
}
///////////////////////////////////////////////////////////////////////////////
: m_cbAllocated(0),
m_cbSize(0),
m_cbOffset(0),
{
}
GuestProcessStream::~GuestProcessStream(void)
{
Destroy();
}
/**
* Adds data to the internal parser buffer. Useful if there
* are multiple rounds of adding data needed.
*
* @return IPRT status code.
* @param pbData Pointer to data to add.
* @param cbData Size (in bytes) of data to add.
*/
{
int rc = VINF_SUCCESS;
/* Rewind the buffer if it's empty. */
if (fAddToSet)
m_cbSize = m_cbOffset = 0;
/* Try and see if we can simply append the data. */
{
}
else
{
/* Move any buffered data to the front. */
if (cbInBuf == 0)
m_cbSize = m_cbOffset = 0;
else if (m_cbOffset) /* Do we have something to move? */
{
m_cbOffset = 0;
}
/* Do we need to grow the buffer? */
{
if (pvNew)
{
}
else
rc = VERR_NO_MEMORY;
}
/* Finally, copy the data. */
if (RT_SUCCESS(rc))
{
{
}
else
}
}
return rc;
}
/**
* Destroys the internal data buffer.
*/
void GuestProcessStream::Destroy(void)
{
if (m_pbBuffer)
{
m_pbBuffer = NULL;
}
m_cbAllocated = 0;
m_cbSize = 0;
m_cbOffset = 0;
}
#ifdef DEBUG
{
LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
if (RT_SUCCESS(rc))
{
}
}
#endif
/**
* Tries to parse the next upcoming pair block within the internal
* buffer.
*
* Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
* completely parsed already.
*
* Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
* stored in stream block) but still contains incomplete (unterminated)
* data.
*
* Returns VINF_SUCCESS if current block was parsed until the next upcoming
* block (with zero or more pairs stored in stream block).
*
* @return IPRT status code.
* @param streamBlock Reference to guest stream block to fill.
*
*/
{
if ( !m_pbBuffer
|| !m_cbSize)
{
return VERR_NO_DATA;
}
if (m_cbOffset == m_cbSize)
return VERR_NO_DATA;
int rc = VINF_SUCCESS;
while (*pszStart)
{
{
rc = VERR_MORE_DATA;
break;
}
else
{
if (pszSep)
{
rc = VERR_MORE_DATA;
break;
}
/* Terminate the separator so that we can
* use pszStart as our key from now on. */
*pszSep = '\0';
if (RT_FAILURE(rc))
return rc;
}
/* Next pair. */
}
/* If we did not do any movement but we have stuff left
* in our buffer just skip the current termination so that
* we can try next time. */
if ( !uDistance
&& *pszStart == '\0'
&& m_cbOffset < m_cbSize)
{
uDistance++;
}
m_cbOffset += uDistance;
return rc;
}
{
}
{
}
{
return rc;
}
void GuestBase::baseUninit(void)
{
/* No return value. */
}
int GuestBase::cancelWaitEvents(void)
{
if (RT_SUCCESS(rc))
{
{
{
/*
* Just cancel the event, but don't remove it from the
* wait events map. Don't delete it though, this (hopefully)
* is done by the caller using unregisterWaitEvent().
*/
itEvents++;
}
}
if (RT_SUCCESS(rc))
}
return rc;
}
{
int vrc = VINF_SUCCESS;
try
{
LogFlowFunc(("uFunc=%RU32, cParms=%RU32\n",
{
break;
case GUEST_MSG_REPLY:
{
{
/* pSvcCb->mpaParms[0] always contains the context ID. */
}
else
break;
}
default:
break;
}
}
{
}
catch (int rc)
{
}
return vrc;
}
{
return VERR_INVALID_PARAMETER;
if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
uCount = 0;
#if 0
LogFlowThisFunc(("mNextContextID=%RU32, uSessionID=%RU32, uObjectID=%RU32, uCount=%RU32, uNewContextID=%RU32\n",
#endif
return VINF_SUCCESS;
}
{
}
const GuestEventTypes &lstEvents,
{
if (RT_FAILURE(rc))
return rc;
if (RT_SUCCESS(rc))
{
try
{
/* Insert event into matching event group. This is for faster per-group
* lookup of all events later. */
{
/** @todo Check for key collision. */
}
/* Register event in regular event list. */
/** @todo Check for key collisions. */
}
{
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS(rc))
}
return rc;
}
{
#ifdef DEBUG
#endif
if (RT_SUCCESS(rc))
{
{
{
#ifdef DEBUG
LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %RU32: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
#endif
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc2))
{
/* Remove the event from all other event groups (except the
* original one!) because it was signalled. */
{
{
/* Get current event group. */
/* Lookup event in event group. */
LogFlowThisFunc(("%zu events for type=%ld left\n",
}
}
/* Remove the event from the passed-in event group. */
}
else
itEvents++;
#ifdef DEBUG
cEvents++;
#endif
}
}
if (RT_SUCCESS(rc))
}
#ifdef DEBUG
#endif
return rc;
}
{
if (RT_SUCCESS(guestRc))
0 /* Guest rc */, pPayload);
}
const GuestWaitEventPayload *pPayload)
{
/* pPayload is optional. */
int rc2;
{
LogFlowThisFunc(("Signalling event=%p (CID %RU32, rc=%Rrc, guestRc=%Rrc, pPayload=%p) ...\n",
}
else
return rc2;
}
{
if (!pEvent) /* Nothing to unregister. */
return;
if (RT_SUCCESS(rc))
{
{
/** @todo Slow O(n) lookup. Optimize this. */
{
{
break;
}
else
itCurEvent++;
}
}
delete pEvent;
if (RT_SUCCESS(rc))
}
}
/**
* Waits for a formerly registered guest event.
*
* @return IPRT status code.
* @param pEvent Pointer to event to wait for.
* @param uTimeoutMS Timeout (in ms) for waiting.
* @param pType Event type of following IEvent.
* Optional.
* @param ppEvent Pointer to IEvent which got triggered
* for this event. Optional.
*/
{
/* pType is optional. */
/* ppEvent is optional. */
if (RT_SUCCESS(vrc))
{
{
if (pType)
{
}
if ( RT_SUCCESS(vrc)
&& ppEvent)
}
}
return vrc;
}
GuestObject::GuestObject(void)
mObjectID(0)
{
}
GuestObject::~GuestObject(void)
{
}
{
return VINF_SUCCESS;
}
{
}
{
#ifndef VBOX_GUESTCTRL_TEST_CASE
int vrc = VERR_HGCM_SERVICE_NOT_FOUND;
/* Forward the information to the VMM device. */
if (pVMMDev)
{
if (RT_FAILURE(vrc))
{
/** @todo What to do here? */
}
}
#else
/* Not needed within testcases. */
int vrc = VINF_SUCCESS;
#endif
return vrc;
}
: mfAborted(false),
mCID(0),
{
}
GuestWaitEventBase::~GuestWaitEventBase(void)
{
}
{
return RTSemEventCreate(&mEventSem);
}
const GuestWaitEventPayload *pPayload)
{
if (ASMAtomicReadBool(&mfAborted))
return VERR_CANCELLED;
#ifdef VBOX_STRICT
if (rc == VERR_GSTCTL_GUEST_ERROR)
AssertMsg(RT_FAILURE(guestRc), ("Guest error indicated but no actual guest error set (%Rrc)\n", guestRc));
else
AssertMsg(RT_SUCCESS(guestRc), ("No guest error indicated but actual guest error set (%Rrc)\n", guestRc));
#endif
int rc2;
if (pPayload)
else
rc2 = VINF_SUCCESS;
if (RT_SUCCESS(rc2))
{
}
return rc2;
}
{
int rc = VINF_SUCCESS;
if (ASMAtomicReadBool(&mfAborted))
rc = VERR_CANCELLED;
if (RT_SUCCESS(rc))
{
if (!uTimeoutMS)
if (ASMAtomicReadBool(&mfAborted))
rc = VERR_CANCELLED;
if (RT_SUCCESS(rc))
{
/* If waiting succeeded, return the overall
* result code. */
}
}
return rc;
}
const GuestEventTypes &lstEvents)
{
}
{
}
GuestWaitEvent::~GuestWaitEvent(void)
{
}
/**
* Cancels the event.
*/
int GuestWaitEvent::Cancel(void)
{
ASMAtomicWriteBool(&mfAborted, true);
#ifdef DEBUG_andy
LogFlowThisFunc(("Cancelling %p ...\n"));
#endif
return RTSemEventSignal(mEventSem);
}
{
}
/**
* Signals the event.
*
* @return IPRT status code.
* @param pEvent Public IEvent to associate.
* Optional.
*/
{
if (pEvent)
return RTSemEventSignal(mEventSem);
}