GuestSessionImpl.cpp revision 18e3d4704ed7b57260b0d4e3ea12c224b3f81840
/* $Id$ */
/** @file
* VirtualBox Main - Guest session handling.
*/
/*
* Copyright (C) 2012-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 "GuestImpl.h"
#include "GuestSessionImpl.h"
#include "GuestCtrlImplPrivate.h"
#include "VirtualBoxErrorInfoImpl.h"
#include "Global.h"
#include "AutoCaller.h"
#include "ProgressImpl.h"
#include "VBoxEvents.h"
#include "VMMDev.h"
#include <memory> /* For auto_ptr. */
#ifdef LOG_GROUP
#endif
#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
/**
* Base class representing an internal
* asynchronous session task.
*/
class GuestSessionTaskInternal
{
public:
mRC(VINF_SUCCESS) { }
virtual ~GuestSessionTaskInternal(void) { }
protected:
int mRC;
};
/**
* Class for asynchronously opening a guest session.
*/
class GuestSessionTaskInternalOpen : public GuestSessionTaskInternal
{
public:
: GuestSessionTaskInternal(pSession) { }
};
/**
* Internal listener class to serve events in an
* active manner, e.g. without polling delays.
*/
class GuestSessionListener
{
public:
GuestSessionListener(void)
{
}
{
return S_OK;
}
void uninit(void)
{
}
{
switch (aType)
{
{
#ifdef DEBUG_andy
LogFlowFunc(("Signalling events of type=%RU32, session=%p resulted in rc=%Rrc\n",
#endif
break;
}
default:
break;
}
return S_OK;
}
private:
};
// constructor / destructor
/////////////////////////////////////////////////////////////////////////////
{
return BaseFinalConstruct();
}
void GuestSession::FinalRelease(void)
{
uninit();
}
// public initializer/uninitializer for internal purposes only
/////////////////////////////////////////////////////////////////////////////
/**
* Initializes a guest session but does *not* open in on the guest side
* yet. This needs to be done via the openSession() / openSessionAsync calls.
*
* @return IPRT status code.
** @todo Docs!
*/
const GuestCredentials &guestCreds)
{
LogFlowThisFunc(("pGuest=%p, ssInfo=%p, guestCreds=%p\n",
/* Enclose the state transition NotReady->InInit->Ready. */
AutoInitSpan autoInitSpan(this);
#ifndef VBOX_WITH_GUEST_CONTROL
return VINF_SUCCESS;
#else
/* Copy over startup info. */
/** @todo Use an overloaded copy operator. Later. */
/** @todo Use an overloaded copy operator. Later. */
mData.mNumObjects = 0;
if (RT_SUCCESS(rc))
{
rc = VERR_NO_MEMORY;
else
{
}
}
if (RT_SUCCESS(rc))
{
try
{
{
TRUE /* Active listener */);
{
}
else
}
else
}
{
rc = VERR_NO_MEMORY;
}
}
if (RT_SUCCESS(rc))
{
/* Confirm a successful initialization when it's the case. */
}
else
LogFlowThisFunc(("mName=%s, mID=%RU32, mIsInternal=%RTbool, rc=%Rrc\n",
return rc;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
/**
* Uninitializes the instance.
* Called from FinalRelease().
*/
void GuestSession::uninit(void)
{
/* Enclose the state transition Ready->InUninit->NotReady. */
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
return;
int rc = VINF_SUCCESS;
#ifdef VBOX_WITH_GUEST_CONTROL
LogFlowThisFunc(("Closing directories (%zu total)\n",
{
mData.mNumObjects--;
}
LogFlowThisFunc(("Closing files (%zu total)\n",
{
mData.mNumObjects--;
}
LogFlowThisFunc(("Closing processes (%zu total)\n",
{
mData.mNumObjects--;
}
baseUninit();
#endif /* VBOX_WITH_GUEST_CONTROL */
}
/////////////////////////////////////////////////////////////////////////////
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
LogFlowThisFunc(("[%s]: cEnvVars=%RU32\n",
{
}
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
int rc = VINF_SUCCESS;
{
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
// no need to lock - lifetime constant
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
// private methods
///////////////////////////////////////////////////////////////////////////////
{
/* Guest Additions < 4.3 don't support closing dedicated
guest sessions, skip. */
{
LogFlowThisFunc(("Installed Guest Additions don't support closing dedicated sessions, skipping\n"));
return VINF_SUCCESS;
}
/** @todo uFlags validation. */
{
LogFlowThisFunc(("Session ID=%RU32 not started (anymore), status now is: %RU32\n",
return VINF_SUCCESS;
}
int vrc;
try
{
eventTypes, &pEvent);
}
{
}
if (RT_FAILURE(vrc))
return vrc;
LogFlowThisFunc(("Sending closing request to guest session ID=%RU32, uFlags=%x\n",
int i = 0;
if (RT_SUCCESS(vrc))
return vrc;
}
{
LogFlowThisFunc(("strPath=%s, uMode=%x, uFlags=%x\n",
int vrc = VINF_SUCCESS;
try
{
/* Construct arguments. */
if (uFlags)
{
procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */
else
}
if (uMode)
{
char szMode[16];
{
}
else
}
}
{
}
if (RT_SUCCESS(vrc))
return vrc;
}
{
{
if (pDir)
return true;
}
return false;
}
int GuestSession::directoryQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc)
{
if (RT_SUCCESS(vrc))
{
}
return vrc;
}
{
int rc = VERR_NOT_FOUND;
{
{
/* Make sure to consume the pointer before the one of the
* iterator gets released. */
LogFlowFunc(("Removing directory \"%s\" (Session: %RU32) (now total %zu processes, %RU32 objects)\n",
Utf8Str(strName).c_str(), mData.mSession.mID, mData.mDirectories.size() - 1, mData.mNumObjects - 1));
mData.mNumObjects--;
break;
}
itDirs++;
}
return rc;
}
int *pGuestRc)
{
&pEvent);
if (RT_FAILURE(vrc))
return vrc;
/* Prepare HGCM call. */
int i = 0;
if (RT_SUCCESS(vrc))
{
if ( vrc == VERR_GSTCTL_GUEST_ERROR
&& pGuestRc)
}
return vrc;
}
{
LogFlowThisFunc(("strTemplate=%s, strPath=%s, fDirectory=%RTbool\n",
int vrc = VINF_SUCCESS;
try
{
if (fDirectory)
{
}
}
{
}
/** @todo Use an internal HGCM command for this operation, since
* we now can run in a user-dedicated session. */
if (RT_SUCCESS(vrc))
&guestRc);
if ( RT_SUCCESS(vrc)
&& RT_SUCCESS(guestRc))
{
{
if (RT_FAILURE(vrc))
{
}
}
else
vrc = VERR_NO_DATA;
if (RT_SUCCESS(vrc))
}
if (pGuestRc)
return vrc;
}
{
LogFlowThisFunc(("strPath=%s, strPath=%s, uFlags=%x\n",
int rc = VERR_MAX_PROCS_REACHED;
return rc;
/* Create a new (host-based) directory ID and assign it. */
for (;;)
{
/* Is the directory ID already used? */
{
/* Callback with context ID was not found. This means
* we can use this context ID for our new callback we want
* to add below. */
rc = VINF_SUCCESS;
break;
}
uNewDirID++;
if (uNewDirID == VBOX_GUESTCTRL_MAX_OBJECTS)
uNewDirID = 0;
if (++uTries == UINT32_MAX)
break; /* Don't try too hard. */
}
if (RT_FAILURE(rc))
return rc;
/* Create the directory object. */
return VERR_COM_UNEXPECTED;
if (RT_FAILURE(vrc))
return vrc;
/*
* Since this is a synchronous guest call we have to
* register the file object first, releasing the session's
* lock and then proceed with the actual opening command
* -- otherwise the file's opening callback would hang
* because the session's lock still is in place.
*/
try
{
/* Add the created directory to our map. */
mData.mNumObjects++;
LogFlowFunc(("Added new guest directory \"%s\" (Session: %RU32) (now total %zu dirs, %RU32 objects)\n",
/** @todo Fire off a VBoxEventType_OnGuestDirectoryRegistered event? */
}
{
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS(rc))
{
/* Nothing further to do here yet. */
if (pGuestRc)
*pGuestRc = VINF_SUCCESS;
}
return vrc;
}
int GuestSession::dispatchToDirectory(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
{
return VERR_INVALID_PARAMETER;
#ifdef DEBUG
LogFlowFunc(("uDirID=%RU32 (%zu total)\n",
#endif
int rc;
{
}
else
rc = VERR_NOT_FOUND;
return rc;
}
{
#ifdef DEBUG
LogFlowFunc(("uFileID=%RU32 (%zu total)\n",
#endif
int rc;
{
}
else
rc = VERR_NOT_FOUND;
return rc;
}
int GuestSession::dispatchToObject(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
{
int rc;
/* Since we don't know which type the object is, we need to through all
* all objects. */
/** @todo Speed this up by adding an object type to the callback context! */
{
{
}
else
{
{
}
else
rc = VERR_NOT_FOUND;
}
}
else
{
}
return rc;
}
int GuestSession::dispatchToProcess(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
{
#ifdef DEBUG
LogFlowFunc(("uProcessID=%RU32 (%zu total)\n",
#endif
int rc;
{
#ifdef DEBUG_andy
#endif
/* Set protocol version so that pSvcCb can
* be interpreted right. */
}
else
rc = VERR_NOT_FOUND;
return rc;
}
{
#ifdef DEBUG
LogFlowThisFunc(("sessionID=%RU32, CID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
#endif
int rc;
{
case GUEST_DISCONNECTED:
/** @todo Handle closing all guest objects. */
break;
case GUEST_SESSION_NOTIFY: /* Guest Additions >= 4.3.0. */
{
break;
}
default:
/* Silently skip unknown callbacks. */
break;
}
return rc;
}
{
{
if (pFile)
return true;
}
return false;
}
{
int rc = VERR_NOT_FOUND;
{
{
/* Make sure to consume the pointer before the one of thfe
* iterator gets released. */
LogFlowThisFunc(("Removing guest file \"%s\" (Session: %RU32) (now total %zu files, %RU32 objects)\n",
mData.mNumObjects--;
false /* Unregistered */);
break;
}
itFiles++;
}
return rc;
}
{
int vrc = VINF_SUCCESS;
try
{
}
{
}
if (RT_SUCCESS(vrc))
return vrc;
}
{
LogFlowThisFunc(("strPath=%s, strOpenMode=%s, strDisposition=%s, uCreationMode=%x, uOffset=%RU64\n",
/* Guest Additions < 4.3 don't support handling
guest files, skip. */
{
LogFlowThisFunc(("Installed Guest Additions don't support handling guest files, skipping\n"));
return VERR_NOT_SUPPORTED;
}
int rc = VERR_MAX_PROCS_REACHED;
return rc;
/* Create a new (host-based) file ID and assign it. */
uint32_t uNewFileID = 0;
for (;;)
{
/* Is the file ID already used? */
{
/* Callback with context ID was not found. This means
* we can use this context ID for our new callback we want
* to add below. */
rc = VINF_SUCCESS;
break;
}
uNewFileID++;
if (uNewFileID == VBOX_GUESTCTRL_MAX_OBJECTS)
uNewFileID = 0;
if (++uTries == UINT32_MAX)
break; /* Don't try too hard. */
}
if (RT_FAILURE(rc))
return rc;
/* Create the directory object. */
return VERR_COM_UNEXPECTED;
if (RT_FAILURE(rc))
return rc;
/*
* Since this is a synchronous guest call we have to
* register the file object first, releasing the session's
* lock and then proceed with the actual opening command
* -- otherwise the file's opening callback would hang
* because the session's lock still is in place.
*/
try
{
/* Add the created file to our vector. */
mData.mNumObjects++;
LogFlowFunc(("Added new guest file \"%s\" (Session: %RU32) (now total %zu files, %RU32 objects)\n",
true /* Registered */);
}
{
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS(rc))
{
int guestRc;
if ( rc == VERR_GSTCTL_GUEST_ERROR
&& pGuestRc)
{
}
}
return rc;
}
int GuestSession::fileQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc)
{
if (RT_SUCCESS(vrc))
{
}
return vrc;
}
{
if (RT_SUCCESS(vrc))
return vrc;
}
int GuestSession::fsQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc)
{
int vrc = VINF_SUCCESS;
/** @todo Merge this with IGuestFile::queryInfo(). */
try
{
/* Construct arguments. */
}
{
}
if (RT_SUCCESS(vrc))
&guestRc);
if ( RT_SUCCESS(vrc)
&& RT_SUCCESS(guestRc))
{
else
vrc = VERR_NO_DATA;
}
if ( vrc == VERR_GSTCTL_GUEST_ERROR
&& pGuestRc)
LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n",
return vrc;
}
{
return mData.mCredentials;
}
{
return mData.mEnvironment;
}
{
}
/* static */
{
/** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
switch (guestRc)
{
case VERR_INVALID_VM_HANDLE:
break;
break;
break;
case VERR_TIMEOUT:
break;
case VERR_CANCELLED:
break;
case VERR_PERMISSION_DENIED:
break;
case VERR_MAX_PROCS_REACHED:
break;
case VERR_NOT_EQUAL: /** @todo Imprecise to the user; can mean anything and all. */
break;
case VERR_NOT_FOUND:
break;
default:
break;
}
return strError;
}
/**
* Checks if this session is ready state where it can handle
* all session-bound actions (like guest processes, guest files).
* Only used by official API methods. Will set an external
* error when not ready.
*/
{
/** @todo Be a bit more informative. */
return S_OK;
}
/**
* Called by IGuest right before this session gets removed from
* the public session list.
*/
int GuestSession::onRemove(void)
{
int vrc = VINF_SUCCESS;
/*
* Note: The event source stuff holds references to this object,
* so make sure that this is cleaned up *before* calling uninit.
*/
if (!mEventSource.isNull())
{
}
return vrc;
}
/** No locking! */
int GuestSession::onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
{
/* pCallback is optional. */
return VERR_INVALID_PARAMETER;
/* pSvcCb->mpaParms[0] always contains the context ID. */
LogFlowThisFunc(("ID=%RU32, uType=%RU32, guestRc=%Rrc\n",
{
break;
break;
break;
break;
break;
break;
default:
break;
}
if (RT_SUCCESS(vrc))
{
if (RT_FAILURE(guestRc))
}
/* Set the session status. */
if (RT_SUCCESS(vrc))
return vrc;
}
{
LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
/* Guest Additions < 4.3 don't support opening dedicated
guest sessions. Simply return success here. */
{
LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n"));
return VINF_SUCCESS;
}
return VINF_SUCCESS;
/** @todo mData.mSession.uFlags validation. */
/* Set current session status. */
int vrc;
try
{
eventTypes, &pEvent);
}
{
}
if (RT_FAILURE(vrc))
return vrc;
int i = 0;
if (RT_SUCCESS(vrc))
{
30 * 1000 /* 30s timeout */,
}
else
{
/*
* Unable to start guest session - update its current state.
* Since there is no (official API) way to recover a failed guest session
* this also marks the end state. Internally just calling this
* same function again will work though.
*/
}
return vrc;
}
int GuestSession::startSessionAsync(void)
{
int vrc;
try
{
/* Asynchronously open the session on the guest by kicking off a
* worker thread. */
"gctlSesStart");
if (RT_SUCCESS(vrc))
{
/* pTask is now owned by openSessionThread(), so release it. */
}
}
{
}
return vrc;
}
/* static */
{
std::auto_ptr<GuestSessionTaskInternalOpen> pTask(static_cast<GuestSessionTaskInternalOpen*>(pvUser));
/* Nothing to do here anymore. */
return vrc;
}
{
LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
&pEvent);
if (RT_FAILURE(vrc))
return vrc;
/* Prepare HGCM call. */
int i = 0;
if (RT_SUCCESS(vrc))
{
if ( vrc == VERR_GSTCTL_GUEST_ERROR
&& pGuestRc)
}
return vrc;
}
{
int rc = VERR_NOT_FOUND;
{
{
#ifdef DEBUG_andy
#endif
/* Make sure to consume the pointer before the one of the
* iterator gets released. */
LogFlowFunc(("Removing process ID=%RU32 (Session: %RU32), guest PID=%RU32 (now total %zu processes, %RU32 objects)\n",
pProcess->getObjectID(), mData.mSession.mID, uPID, mData.mProcesses.size() - 1, mData.mNumObjects - 1));
mData.mNumObjects--;
uPID, false /* Process unregistered */);
break;
}
itProcs++;
}
return rc;
}
/**
* Creates but does *not* start the process yet. See GuestProcess::startProcess() or
* GuestProcess::startProcessAsync() for that.
*
* @return IPRT status code.
* @param procInfo
* @param pProcess
*/
int GuestSession::processCreateExInteral(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
{
LogFlowFunc(("mCmd=%s, mFlags=%x, mTimeoutMS=%RU32\n",
#ifdef DEBUG
{
LogFlowFunc(("Arguments:"));
{
it++;
}
LogFlow(("\n"));
}
#endif
/* Validate flags. */
{
{
return VERR_INVALID_PARAMETER;
}
}
)
)
{
return VERR_INVALID_PARAMETER;
}
/* Adjust timeout. If set to 0, we define
* an infinite timeout. */
if (procInfo.mTimeoutMS == 0)
/** @tood Implement process priority + affinity. */
int rc = VERR_MAX_PROCS_REACHED;
return rc;
/* Create a new (host-based) process ID and assign it. */
uint32_t uNewProcessID = 0;
for (;;)
{
/* Is the context ID already used? */
{
/* Callback with context ID was not found. This means
* we can use this context ID for our new callback we want
* to add below. */
rc = VINF_SUCCESS;
break;
}
uNewProcessID = 0;
if (++uTries == VBOX_GUESTCTRL_MAX_OBJECTS)
break; /* Don't try too hard. */
}
if (RT_FAILURE(rc))
return rc;
/* Create the process object. */
return VERR_COM_UNEXPECTED;
if (RT_FAILURE(rc))
return rc;
/* Add the created process to our map. */
try
{
mData.mNumObjects++;
LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes, %RU32 objects)\n",
0 /* PID */, true /* Process registered */);
}
{
rc = VERR_NO_MEMORY;
}
return rc;
}
{
{
if (pProcess)
return true;
}
return false;
}
{
AssertReturn(uPID, false);
/* pProcess is optional. */
{
if (procCaller.rc())
return VERR_COM_INVALID_OBJECT_STATE;
{
if (pProcess)
return VINF_SUCCESS;
}
}
return VERR_NOT_FOUND;
}
{
#ifndef VBOX_GUESTCTRL_TEST_CASE
/* Forward the information to the VMM device. */
if (RT_FAILURE(vrc))
{
/** @todo What to do here? */
}
#else
/* Not needed within testcases. */
int vrc = VINF_SUCCESS;
#endif
return vrc;
}
/* static */
{
}
/* Does not do locking; caller is responsible for that! */
{
LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
{
/* Do not allow overwriting an already set error. If this happens
}
else
{
}
return VINF_SUCCESS;
}
{
/*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
/* Note: No write locking here -- already done in the caller. */
int vrc = VINF_SUCCESS;
/*if (mData.mWaitEvent)
vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
return vrc;
}
{
/* Create the progress object. */
return VERR_COM_UNEXPECTED;
TRUE /* aCancelable */);
return VERR_COM_UNEXPECTED;
/* Initialize our worker task. */
if (RT_FAILURE(rc))
return rc;
/* Don't destruct on success. */
return rc;
}
/**
* This is necessary to know which guest control protocol version to use,
* among other things (later).
*
* @return IPRT status code.
*/
int GuestSession::queryInfo(void)
{
/*
* Try querying the guest control protocol version running on the guest.
* This is done using the Guest Additions version
*/
#ifdef DEBUG_andy
/* Hardcode the to-used protocol version; nice for testing side effects. */
#else
/* VBox 5.0 and up. */
uVBoxMajor >= 5
/* VBox 4.3 and up. */
? 2 /* Guest control 2.0. */
: 1; /* Legacy guest control (VBox < 4.3). */
/* Build revision is ignored. */
#endif
LogFlowThisFunc(("uVerAdditions=%RU32 (%RU32.%RU32), mProtocolVersion=%RU32\n",
/* Tell the user but don't bitch too often. */
static short s_gctrlLegacyWarning = 0;
LogRel((tr("Warning: Guest Additions are older (%ld.%ld) than host capabilities for guest control, please upgrade them. Using protocol version %ld now\n"),
return VINF_SUCCESS;
}
int GuestSession::waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *pGuestRc)
{
/*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n",
fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));*/
/* Did some error occur before? Then skip waiting and return. */
{
AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
if (pGuestRc)
return VERR_GSTCTL_GUEST_ERROR;
}
/* Guest Additions < 4.3 don't support session handling, skip. */
{
LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
return VINF_SUCCESS;
}
{
{
case GuestSessionStatus_Down:
break;
break;
case GuestSessionStatus_Error:
/* Handled above. */
break;
break;
/* Do the waiting below. */
break;
default:
return VERR_NOT_IMPLEMENTED;
}
}
else if (fWaitFlags & GuestSessionWaitForFlag_Start)
{
{
case GuestSessionStatus_Down:
break;
case GuestSessionStatus_Error:
break;
break;
/* Do the waiting below. */
break;
default:
return VERR_NOT_IMPLEMENTED;
}
}
LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
/* No waiting needed? Return immediately using the last set error. */
{
if (pGuestRc)
}
int vrc;
try
{
eventTypes, &pEvent);
}
{
}
if (RT_FAILURE(vrc))
return vrc;
if (RT_SUCCESS(vrc))
{
switch (sessionStatus)
{
break;
break;
break;
case GuestSessionStatus_Down:
break;
case GuestSessionStatus_Error:
break;
default:
break;
}
}
return vrc;
}
int GuestSession::waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
{
if (RT_SUCCESS(vrc))
{
if (pSessionStatus)
if (RT_FAILURE((int)lGuestRc))
if (pGuestRc)
LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
}
return vrc;
}
// implementation of public methods
/////////////////////////////////////////////////////////////////////////////
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
/* Close session on guest. */
int guestRc = VINF_SUCCESS;
&guestRc);
/* On failure don't return here, instead do all the cleanup
* work first and then return an error. */
/* Remove ourselves from the session list. */
rc2 = VINF_SUCCESS;
if (RT_SUCCESS(rc))
LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n",
if (RT_FAILURE(rc))
{
if (rc == VERR_GSTCTL_GUEST_ERROR)
return setError(VBOX_E_IPRT_ERROR,
}
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::CopyFrom(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(CopyFileFlag_T, aFlags), IProgress **aProgress)
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
if (aFlags)
{
}
try
{
int rc = startTaskAsync(Utf8StrFmt(tr("Copying \"%ls\" from guest to \"%ls\" on the host"), aSource, aDest),
if (RT_SUCCESS(rc))
{
/* Return progress to the caller. */
}
else
}
{
hr = E_OUTOFMEMORY;
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::CopyTo(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(CopyFileFlag_T, aFlags), IProgress **aProgress)
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
if (aFlags)
{
}
try
{
int rc = startTaskAsync(Utf8StrFmt(tr("Copying \"%ls\" from host to \"%ls\" on the guest"), aSource, aDest),
if (RT_SUCCESS(rc))
{
/* Return progress to the caller. */
}
else
}
{
hr = E_OUTOFMEMORY;
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
if (aFlags)
{
if (fFlags)
{
if (!(fFlags & DirectoryCreateFlag_Parents))
}
}
if (RT_FAILURE(rc))
{
switch (rc)
{
case VERR_GSTCTL_GUEST_ERROR:
/** @todo Handle VERR_NOT_EQUAL (meaning process exit code <> 0). */
break;
case VERR_INVALID_PARAMETER:
break;
case VERR_BROKEN_PIPE:
break;
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::DirectoryCreateTemp(IN_BSTR aTemplate, ULONG aMode, IN_BSTR aPath, BOOL aSecure, BSTR *aDirectory)
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
if (RT_SUCCESS(rc))
{
}
else
{
switch (rc)
{
case VERR_GSTCTL_GUEST_ERROR:
break;
default:
hr = setError(VBOX_E_IPRT_ERROR, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
if (RT_SUCCESS(rc))
{
}
else
{
switch (rc)
{
case VERR_GSTCTL_GUEST_ERROR:
break;
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::DirectoryOpen(IN_BSTR aPath, IN_BSTR aFilter, ComSafeArrayIn(DirectoryOpenFlag_T, aFlags), IGuestDirectory **aDirectory)
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
if (aFlags)
{
if (fFlags)
}
if (RT_SUCCESS(rc))
{
/* Return directory object to the caller. */
}
else
{
switch (rc)
{
case VERR_INVALID_PARAMETER:
break;
case VERR_GSTCTL_GUEST_ERROR:
break;
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
if (RT_SUCCESS(vrc))
{
{
if (RT_SUCCESS(vrc))
{
}
}
}
if (RT_FAILURE(vrc))
{
switch (vrc)
{
case VERR_GSTCTL_GUEST_ERROR:
break;
case VERR_NOT_A_DIRECTORY:
break;
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return hr;
/* No flags; only remove the directory when empty. */
int guestRc;
if (RT_FAILURE(vrc))
{
switch (vrc)
{
case VERR_NOT_SUPPORTED:
tr("Handling removing guest directories not supported by installed Guest Additions"));
break;
case VERR_GSTCTL_GUEST_ERROR:
break;
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::DirectoryRemoveRecursive(IN_BSTR aPath, ComSafeArrayIn(DirectoryRemoveRecFlag_T, aFlags), IProgress **aProgress)
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return hr;
TRUE /*aCancelable*/);
return hr;
/* Note: At the moment we don't supply progress information while
* deleting a guest directory recursively. So just complete
* the progress object right now. */
/** @todo Implement progress reporting on guest directory deletion! */
return hr;
/* Remove the directory + all its contents. */
int guestRc;
if (RT_FAILURE(vrc))
{
switch (vrc)
{
case VERR_NOT_SUPPORTED:
tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
break;
case VERR_GSTCTL_GUEST_ERROR:
break;
default:
break;
}
}
else
{
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::DirectoryRename(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(PathRenameFlag_T, aFlags))
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return hr;
/* No flags; only remove the directory when empty. */
int guestRc;
if (RT_FAILURE(vrc))
{
switch (vrc)
{
case VERR_NOT_SUPPORTED:
tr("Handling renaming guest directories not supported by installed Guest Additions"));
break;
case VERR_GSTCTL_GUEST_ERROR:
break;
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::FileCreateTemp(IN_BSTR aTemplate, ULONG aMode, IN_BSTR aPath, BOOL aSecure, IGuestFile **aFile)
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
if (RT_SUCCESS(vrc))
{
return S_OK;
}
switch (vrc)
{
case VERR_GSTCTL_GUEST_ERROR:
break;
case VERR_NOT_A_FILE:
break;
default:
break;
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
int guestRc;
if (RT_FAILURE(vrc))
{
switch (vrc)
{
case VERR_GSTCTL_GUEST_ERROR:
break;
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::FileOpen(IN_BSTR aPath, IN_BSTR aOpenMode, IN_BSTR aDisposition, ULONG aCreationMode, IGuestFile **aFile)
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
0 /* aOffset */, aFile);
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::FileOpenEx(IN_BSTR aPath, IN_BSTR aOpenMode, IN_BSTR aDisposition, IN_BSTR aSharingMode,
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
/* aSharingMode is optional. */
AutoCaller autoCaller(this);
return hr;
/** @todo Validate creation mode. */
uint32_t uCreationMode = 0;
if (RT_FAILURE(vrc))
if (RT_SUCCESS(vrc))
{
/* Return directory object to the caller. */
}
else
{
switch (vrc)
{
case VERR_NOT_SUPPORTED:
tr("Handling guest files not supported by installed Guest Additions"));
break;
case VERR_GSTCTL_GUEST_ERROR:
break;
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
if (RT_SUCCESS(vrc))
{
if (RT_SUCCESS(vrc))
{
}
}
if (RT_FAILURE(vrc))
{
switch (vrc)
{
case VERR_GSTCTL_GUEST_ERROR:
break;
case VERR_NOT_A_FILE:
break;
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
if (RT_SUCCESS(vrc))
{
}
else
{
switch (vrc)
{
case VERR_GSTCTL_GUEST_ERROR:
break;
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::FileRename(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(PathRenameFlag_T, aFlags))
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return hr;
/* No flags; only remove the directory when empty. */
int guestRc;
if (RT_FAILURE(vrc))
{
switch (vrc)
{
case VERR_NOT_SUPPORTED:
tr("Handling renaming guest files not supported by installed Guest Additions"));
break;
case VERR_GSTCTL_GUEST_ERROR:
/** @todo Proper guestRc to text translation needed. */
break;
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::ProcessCreate(IN_BSTR aCommand, ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
ComSafeArrayInArg(aFlags), aTimeoutMS, ProcessPriority_Default, ComSafeArrayAsInParam(affinityIgnored), aProcess);
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::ProcessCreateEx(IN_BSTR aCommand, ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
return hr;
if (aArguments)
{
}
int rc = VINF_SUCCESS;
/*
* Create the process environment:
* - Apply the session environment in a first step, and
* - Apply environment variables specified by this call to
* have the chance of overwriting/deleting session entries.
*/
if (aEnvironment)
{
}
if (RT_SUCCESS(rc))
{
if (aFlags)
{
}
if (aAffinity)
{
{
if (affinity[i])
}
}
if (RT_SUCCESS(rc))
{
/* Return guest session to the caller. */
if (RT_SUCCESS(rc))
}
}
if (RT_FAILURE(rc))
{
switch (rc)
{
case VERR_MAX_PROCS_REACHED:
hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest processes per session (%ld) reached"),
break;
/** @todo Add more errors here. */
default:
break;
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
if (aPID == 0)
AutoCaller autoCaller(this);
if (RT_FAILURE(rc))
/* This will set (*aProcess) to NULL if pProgress is NULL. */
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::SymlinkRead(IN_BSTR aSymlink, ComSafeArrayIn(SymlinkReadFlag_T, aFlags), BSTR *aTarget)
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
#endif /* VBOX_WITH_GUEST_CONTROL */
}
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::WaitFor(ULONG aWaitFlags, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
/*
* Note: Do not hold any locks here while waiting!
*/
if (RT_SUCCESS(vrc))
{
*aReason = waitResult;
}
else
{
switch (vrc)
{
case VERR_GSTCTL_GUEST_ERROR:
break;
case VERR_TIMEOUT:
break;
default:
{
tr("Waiting for guest session \"%s\" failed: %Rrc"),
break;
}
}
}
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
STDMETHODIMP GuestSession::WaitForArray(ComSafeArrayIn(GuestSessionWaitForFlag_T, aFlags), ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
{
#ifndef VBOX_WITH_GUEST_CONTROL
#else
AutoCaller autoCaller(this);
/*
* Note: Do not hold any locks here while waiting!
*/
#endif /* VBOX_WITH_GUEST_CONTROL */
}