summaryrefslogtreecommitdiff
path: root/src/VBox/Main/src-client/GuestSessionImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-client/GuestSessionImpl.cpp')
-rw-r--r--src/VBox/Main/src-client/GuestSessionImpl.cpp2272
1 files changed, 1988 insertions, 284 deletions
diff --git a/src/VBox/Main/src-client/GuestSessionImpl.cpp b/src/VBox/Main/src-client/GuestSessionImpl.cpp
index f24337c6..433ac056 100644
--- a/src/VBox/Main/src-client/GuestSessionImpl.cpp
+++ b/src/VBox/Main/src-client/GuestSessionImpl.cpp
@@ -1,11 +1,10 @@
-
/* $Id: GuestSessionImpl.cpp $ */
/** @file
- * VirtualBox Main - XXX.
+ * VirtualBox Main - Guest session handling.
*/
/*
- * Copyright (C) 2012 Oracle Corporation
+ * 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;
@@ -23,17 +22,22 @@
#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. */
+#include <iprt/cpp/utils.h> /* For unconst(). */
#include <iprt/env.h>
#include <iprt/file.h> /* For CopyTo/From. */
#include <VBox/com/array.h>
+#include <VBox/com/listeners.h>
#include <VBox/version.h>
#ifdef LOG_GROUP
@@ -43,6 +47,96 @@
#include <VBox/log.h>
+/**
+ * Base class representing an internal
+ * asynchronous session task.
+ */
+class GuestSessionTaskInternal
+{
+public:
+
+ GuestSessionTaskInternal(GuestSession *pSession)
+ : mSession(pSession),
+ mRC(VINF_SUCCESS) { }
+
+ virtual ~GuestSessionTaskInternal(void) { }
+
+ int rc(void) const { return mRC; }
+ bool isOk(void) const { return RT_SUCCESS(mRC); }
+ const ComObjPtr<GuestSession> &Session(void) const { return mSession; }
+
+protected:
+
+ const ComObjPtr<GuestSession> mSession;
+ int mRC;
+};
+
+/**
+ * Class for asynchronously opening a guest session.
+ */
+class GuestSessionTaskInternalOpen : public GuestSessionTaskInternal
+{
+public:
+
+ GuestSessionTaskInternalOpen(GuestSession *pSession)
+ : GuestSessionTaskInternal(pSession) { }
+};
+
+/**
+ * Internal listener class to serve events in an
+ * active manner, e.g. without polling delays.
+ */
+class GuestSessionListener
+{
+public:
+
+ GuestSessionListener(void)
+ {
+ }
+
+ HRESULT init(GuestSession *pSession)
+ {
+ AssertPtrReturn(pSession, E_POINTER);
+ mSession = pSession;
+ return S_OK;
+ }
+
+ void uninit(void)
+ {
+ mSession = NULL;
+ }
+
+ STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
+ {
+ switch (aType)
+ {
+ case VBoxEventType_OnGuestSessionStateChanged:
+ {
+ AssertPtrReturn(mSession, E_POINTER);
+ int rc2 = mSession->signalWaitEvent(aType, aEvent);
+#ifdef DEBUG_andy
+ LogFlowFunc(("Signalling events of type=%RU32, session=%p resulted in rc=%Rrc\n",
+ aType, mSession, rc2));
+#endif
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("Unhandled event %RU32\n", aType));
+ break;
+ }
+
+ return S_OK;
+ }
+
+private:
+
+ GuestSession *mSession;
+};
+typedef ListenerImpl<GuestSessionListener, GuestSession*> GuestSessionListenerImpl;
+
+VBOX_LISTENER_DECLARE(GuestSessionListenerImpl)
+
// constructor / destructor
/////////////////////////////////////////////////////////////////////////////
@@ -50,7 +144,7 @@ DEFINE_EMPTY_CTOR_DTOR(GuestSession)
HRESULT GuestSession::FinalConstruct(void)
{
- LogFlowThisFunc(("\n"));
+ LogFlowThisFuncEnter();
return BaseFinalConstruct();
}
@@ -65,32 +159,112 @@ void GuestSession::FinalRelease(void)
// public initializer/uninitializer for internal purposes only
/////////////////////////////////////////////////////////////////////////////
-int GuestSession::init(Guest *aGuest, ULONG aSessionID,
- Utf8Str aUser, Utf8Str aPassword, Utf8Str aDomain, Utf8Str aName)
+/**
+ * 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!
+ */
+int GuestSession::init(Guest *pGuest, const GuestSessionStartupInfo &ssInfo,
+ const GuestCredentials &guestCreds)
{
- LogFlowThisFuncEnter();
-
- AssertPtrReturn(aGuest, VERR_INVALID_POINTER);
+ LogFlowThisFunc(("pGuest=%p, ssInfo=%p, guestCreds=%p\n",
+ pGuest, &ssInfo, &guestCreds));
/* Enclose the state transition NotReady->InInit->Ready. */
AutoInitSpan autoInitSpan(this);
AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
- mData.mTimeout = 30 * 60 * 1000; /* Session timeout is 30 mins by default. */
- mData.mParent = aGuest;
- mData.mId = aSessionID;
+#ifndef VBOX_WITH_GUEST_CONTROL
+ autoInitSpan.setSucceeded();
+ return VINF_SUCCESS;
+#else
+ AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
+
+ mParent = pGuest;
+
+ /* Copy over startup info. */
+ /** @todo Use an overloaded copy operator. Later. */
+ mData.mSession.mID = ssInfo.mID;
+ mData.mSession.mIsInternal = ssInfo.mIsInternal;
+ mData.mSession.mName = ssInfo.mName;
+ mData.mSession.mOpenFlags = ssInfo.mOpenFlags;
+ mData.mSession.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS;
+
+ /** @todo Use an overloaded copy operator. Later. */
+ mData.mCredentials.mUser = guestCreds.mUser;
+ mData.mCredentials.mPassword = guestCreds.mPassword;
+ mData.mCredentials.mDomain = guestCreds.mDomain;
- mData.mCredentials.mUser = aUser;
- mData.mCredentials.mPassword = aPassword;
- mData.mCredentials.mDomain = aDomain;
- mData.mName = aName;
+ mData.mRC = VINF_SUCCESS;
+ mData.mStatus = GuestSessionStatus_Undefined;
mData.mNumObjects = 0;
- /* Confirm a successful initialization when it's the case. */
- autoInitSpan.setSucceeded();
+ HRESULT hr;
- LogFlowFuncLeaveRC(VINF_SUCCESS);
- return VINF_SUCCESS;
+ int rc = queryInfo();
+ if (RT_SUCCESS(rc))
+ {
+ hr = unconst(mEventSource).createObject();
+ if (FAILED(hr))
+ rc = VERR_NO_MEMORY;
+ else
+ {
+ hr = mEventSource->init();
+ if (FAILED(hr))
+ rc = VERR_COM_UNEXPECTED;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ try
+ {
+ GuestSessionListener *pListener = new GuestSessionListener();
+ ComObjPtr<GuestSessionListenerImpl> thisListener;
+ hr = thisListener.createObject();
+ if (SUCCEEDED(hr))
+ hr = thisListener->init(pListener, this);
+
+ if (SUCCEEDED(hr))
+ {
+ com::SafeArray <VBoxEventType_T> eventTypes;
+ eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
+ hr = mEventSource->RegisterListener(thisListener,
+ ComSafeArrayAsInParam(eventTypes),
+ TRUE /* Active listener */);
+ if (SUCCEEDED(hr))
+ {
+ mLocalListener = thisListener;
+
+ rc = RTCritSectInit(&mWaitEventCritSect);
+ AssertRC(rc);
+ }
+ else
+ rc = VERR_COM_UNEXPECTED;
+ }
+ else
+ rc = VERR_COM_UNEXPECTED;
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Confirm a successful initialization when it's the case. */
+ autoInitSpan.setSucceeded();
+ }
+ else
+ autoInitSpan.setFailed();
+
+ LogFlowThisFunc(("mName=%s, mID=%RU32, mIsInternal=%RTbool, rc=%Rrc\n",
+ mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal, rc));
+ return rc;
+#endif /* VBOX_WITH_GUEST_CONTROL */
}
/**
@@ -99,60 +273,59 @@ int GuestSession::init(Guest *aGuest, ULONG aSessionID,
*/
void GuestSession::uninit(void)
{
- LogFlowThisFuncEnter();
-
/* Enclose the state transition Ready->InUninit->NotReady. */
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
return;
+ LogFlowThisFuncEnter();
+
int rc = VINF_SUCCESS;
-#ifndef VBOX_WITH_GUEST_CONTROL
- LogFlowThisFunc(("Closing directories (%RU64 total)\n",
+#ifdef VBOX_WITH_GUEST_CONTROL
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ LogFlowThisFunc(("Closing directories (%zu total)\n",
mData.mDirectories.size()));
for (SessionDirectories::iterator itDirs = mData.mDirectories.begin();
itDirs != mData.mDirectories.end(); ++itDirs)
{
-#ifdef DEBUG
- ULONG cRefs = (*itDirs)->AddRef();
- LogFlowThisFunc(("pFile=%p, cRefs=%RU32\n", (*itDirs), cRefs));
- (*itDirs)->Release();
-#endif
- (*itDirs)->uninit();
+ Assert(mData.mNumObjects);
+ mData.mNumObjects--;
+ itDirs->second->onRemove();
+ itDirs->second->uninit();
}
mData.mDirectories.clear();
- LogFlowThisFunc(("Closing files (%RU64 total)\n",
+ LogFlowThisFunc(("Closing files (%zu total)\n",
mData.mFiles.size()));
for (SessionFiles::iterator itFiles = mData.mFiles.begin();
itFiles != mData.mFiles.end(); ++itFiles)
{
-#ifdef DEBUG
- ULONG cRefs = (*itFiles)->AddRef();
- LogFlowThisFunc(("pFile=%p, cRefs=%RU32\n", (*itFiles), cRefs));
- (*itFiles)->Release();
-#endif
- (*itFiles)->uninit();
+ Assert(mData.mNumObjects);
+ mData.mNumObjects--;
+ itFiles->second->onRemove();
+ itFiles->second->uninit();
}
mData.mFiles.clear();
- LogFlowThisFunc(("Closing processes (%RU64 total)\n",
+ LogFlowThisFunc(("Closing processes (%zu total)\n",
mData.mProcesses.size()));
for (SessionProcesses::iterator itProcs = mData.mProcesses.begin();
itProcs != mData.mProcesses.end(); ++itProcs)
{
-#ifdef DEBUG
- ULONG cRefs = itProcs->second->AddRef();
- LogFlowThisFunc(("pProcess=%p, cRefs=%RU32\n", itProcs->second, cRefs));
- itProcs->second->Release();
-#endif
+ Assert(mData.mNumObjects);
+ mData.mNumObjects--;
+ itProcs->second->onRemove();
itProcs->second->uninit();
}
mData.mProcesses.clear();
- LogFlowThisFunc(("mNumObjects=%RU32\n", mData.mNumObjects));
-#endif
+ AssertMsg(mData.mNumObjects == 0,
+ ("mNumObjects=%RU32 when it should be 0\n", mData.mNumObjects));
+
+ baseUninit();
+#endif /* VBOX_WITH_GUEST_CONTROL */
LogFlowFuncLeaveRC(rc);
}
@@ -175,7 +348,7 @@ STDMETHODIMP GuestSession::COMGETTER(User)(BSTR *aUser)
mData.mCredentials.mUser.cloneTo(aUser);
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowThisFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -196,7 +369,7 @@ STDMETHODIMP GuestSession::COMGETTER(Domain)(BSTR *aDomain)
mData.mCredentials.mDomain.cloneTo(aDomain);
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowThisFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -215,9 +388,9 @@ STDMETHODIMP GuestSession::COMGETTER(Name)(BSTR *aName)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- mData.mName.cloneTo(aName);
+ mData.mSession.mName.cloneTo(aName);
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowThisFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -236,9 +409,30 @@ STDMETHODIMP GuestSession::COMGETTER(Id)(ULONG *aId)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- *aId = mData.mId;
+ *aId = mData.mSession.mID;
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowThisFuncLeave();
+ return S_OK;
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
+STDMETHODIMP GuestSession::COMGETTER(Status)(GuestSessionStatus_T *aStatus)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+ ReturnComNotImplemented();
+#else
+ LogFlowThisFuncEnter();
+
+ CheckComArgOutPointerValid(aStatus);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aStatus = mData.mStatus;
+
+ LogFlowThisFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -259,7 +453,7 @@ STDMETHODIMP GuestSession::COMGETTER(Timeout)(ULONG *aTimeout)
*aTimeout = mData.mTimeout;
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowThisFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -278,7 +472,28 @@ STDMETHODIMP GuestSession::COMSETTER(Timeout)(ULONG aTimeout)
mData.mTimeout = aTimeout;
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowThisFuncLeave();
+ return S_OK;
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
+STDMETHODIMP GuestSession::COMGETTER(ProtocolVersion)(ULONG *aVersion)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+ ReturnComNotImplemented();
+#else
+ LogFlowThisFuncEnter();
+
+ CheckComArgOutPointerValid(aVersion);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aVersion = mData.mProtocolVersion;
+
+ LogFlowThisFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -298,7 +513,8 @@ STDMETHODIMP GuestSession::COMGETTER(Environment)(ComSafeArrayOut(BSTR, aEnviron
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
size_t cEnvVars = mData.mEnvironment.Size();
- LogFlowThisFunc(("%s cEnvVars=%RU32\n", mData.mName.c_str(), cEnvVars));
+ LogFlowThisFunc(("[%s]: cEnvVars=%RU32\n",
+ mData.mSession.mName.c_str(), cEnvVars));
com::SafeArray<BSTR> environment(cEnvVars);
for (size_t i = 0; i < cEnvVars; i++)
@@ -308,7 +524,7 @@ STDMETHODIMP GuestSession::COMGETTER(Environment)(ComSafeArrayOut(BSTR, aEnviron
}
environment.detachTo(ComSafeArrayOutArg(aEnvironment));
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowThisFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -358,7 +574,7 @@ STDMETHODIMP GuestSession::COMGETTER(Processes)(ComSafeArrayOut(IGuestProcess *,
SafeIfaceArray<IGuestProcess> collection(mData.mProcesses);
collection.detachTo(ComSafeArrayOutArg(aProcesses));
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowFunc(("mProcesses=%zu\n", collection.size()));
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -380,7 +596,7 @@ STDMETHODIMP GuestSession::COMGETTER(Directories)(ComSafeArrayOut(IGuestDirector
SafeIfaceArray<IGuestDirectory> collection(mData.mDirectories);
collection.detachTo(ComSafeArrayOutArg(aDirectories));
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowFunc(("mDirectories=%zu\n", collection.size()));
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -402,92 +618,156 @@ STDMETHODIMP GuestSession::COMGETTER(Files)(ComSafeArrayOut(IGuestFile *, aFiles
SafeIfaceArray<IGuestFile> collection(mData.mFiles);
collection.detachTo(ComSafeArrayOutArg(aFiles));
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowFunc(("mFiles=%zu\n", collection.size()));
+ return S_OK;
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
+STDMETHODIMP GuestSession::COMGETTER(EventSource)(IEventSource ** aEventSource)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+ ReturnComNotImplemented();
+#else
+ LogFlowThisFuncEnter();
+
+ CheckComArgOutPointerValid(aEventSource);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ // no need to lock - lifetime constant
+ mEventSource.queryInterfaceTo(aEventSource);
+
+ LogFlowThisFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
// private methods
-/////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
-int GuestSession::directoryRemoveFromList(GuestDirectory *pDirectory)
+int GuestSession::closeSession(uint32_t uFlags, uint32_t uTimeoutMS, int *pGuestRc)
{
+ LogFlowThisFunc(("uFlags=%x, uTimeoutMS=%RU32\n", uFlags, uTimeoutMS));
+
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- for (SessionDirectories::iterator itDirs = mData.mDirectories.begin();
- itDirs != mData.mDirectories.end(); ++itDirs)
+ /* Guest Additions < 4.3 don't support closing dedicated
+ guest sessions, skip. */
+ if (mData.mProtocolVersion < 2)
{
- if (pDirectory == (*itDirs))
- {
- Bstr strName;
- HRESULT hr = (*itDirs)->COMGETTER(DirectoryName)(strName.asOutParam());
- ComAssertComRC(hr);
+ LogFlowThisFunc(("Installed Guest Additions don't support closing dedicated sessions, skipping\n"));
+ return VINF_SUCCESS;
+ }
- Assert(mData.mDirectories.size());
- LogFlowFunc(("Removing directory \"%s\" (Session: %RU32) (now total %ld directories)\n",
- Utf8Str(strName).c_str(), mData.mId, mData.mDirectories.size() - 1));
+ /** @todo uFlags validation. */
- mData.mDirectories.erase(itDirs);
- return VINF_SUCCESS;
- }
+ if (mData.mStatus != GuestSessionStatus_Started)
+ {
+ LogFlowThisFunc(("Session ID=%RU32 not started (anymore), status now is: %RU32\n",
+ mData.mSession.mID, mData.mStatus));
+ return VINF_SUCCESS;
}
- return VERR_NOT_FOUND;
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
+ try
+ {
+ eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
+
+ vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
+ eventTypes, &pEvent);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ LogFlowThisFunc(("Sending closing request to guest session ID=%RU32, uFlags=%x\n",
+ mData.mSession.mID, uFlags));
+
+ VBOXHGCMSVCPARM paParms[4];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setUInt32(uFlags);
+
+ alock.release(); /* Drop the write lock before waiting. */
+
+ vrc = sendCommand(HOST_SESSION_CLOSE, i, paParms);
+ if (RT_SUCCESS(vrc))
+ vrc = waitForStatusChange(pEvent, GuestSessionWaitForFlag_Terminate, uTimeoutMS,
+ NULL /* Session status */, pGuestRc);
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
}
-int GuestSession::directoryCreateInternal(const Utf8Str &strPath, uint32_t uMode, uint32_t uFlags, int *pGuestRc)
+int GuestSession::directoryCreateInternal(const Utf8Str &strPath, uint32_t uMode,
+ uint32_t uFlags, int *pGuestRc)
{
LogFlowThisFunc(("strPath=%s, uMode=%x, uFlags=%x\n",
strPath.c_str(), uMode, uFlags));
+ int vrc = VINF_SUCCESS;
+
GuestProcessStartupInfo procInfo;
procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_MKDIR);
procInfo.mFlags = ProcessCreateFlag_Hidden;
- int vrc = VINF_SUCCESS;
-
- /* Construct arguments. */
- if (uFlags & DirectoryCreateFlag_Parents)
- procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */
- if (uMode)
+ try
{
- procInfo.mArguments.push_back(Utf8Str("--mode")); /* Set the creation mode. */
-
- char szMode[16];
- if (RTStrPrintf(szMode, sizeof(szMode), "%o", uMode))
+ /* Construct arguments. */
+ if (uFlags)
{
- procInfo.mArguments.push_back(Utf8Str(szMode));
+ if (uFlags & DirectoryCreateFlag_Parents)
+ procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */
+ else
+ vrc = VERR_INVALID_PARAMETER;
}
- else
- vrc = VERR_BUFFER_OVERFLOW;
- }
- procInfo.mArguments.push_back(strPath); /* The directory we want to create. */
- int guestRc;
- if (RT_SUCCESS(vrc))
- {
- GuestProcessTool procTool;
- vrc = procTool.Init(this, procInfo, false /* Async */, &guestRc);
- if (RT_SUCCESS(vrc))
+ if (uMode)
{
- if (RT_SUCCESS(guestRc))
- vrc = procTool.Wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
- }
+ procInfo.mArguments.push_back(Utf8Str("--mode")); /* Set the creation mode. */
- if (RT_SUCCESS(vrc))
- {
- if (RT_SUCCESS(guestRc))
- guestRc = procTool.TerminatedOk(NULL /* Exit code */);
+ char szMode[16];
+ if (RTStrPrintf(szMode, sizeof(szMode), "%o", uMode))
+ {
+ procInfo.mArguments.push_back(Utf8Str(szMode));
+ }
+ else
+ vrc = VERR_BUFFER_OVERFLOW;
}
-
- if (pGuestRc)
- *pGuestRc = guestRc;
+ procInfo.mArguments.push_back(strPath); /* The directory we want to create. */
}
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(vrc))
+ vrc = GuestProcessTool::Run(this, procInfo, pGuestRc);
LogFlowFuncLeaveRC(vrc);
- if (RT_FAILURE(vrc))
- return vrc;
- return RT_SUCCESS(guestRc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Special guest control rc needed! */
+ return vrc;
+}
+
+inline bool GuestSession::directoryExists(uint32_t uDirID, ComObjPtr<GuestDirectory> *pDir)
+{
+ SessionDirectories::const_iterator it = mData.mDirectories.find(uDirID);
+ if (it != mData.mDirectories.end())
+ {
+ if (pDir)
+ *pDir = it->second;
+ return true;
+ }
+ return false;
}
int GuestSession::directoryQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc)
@@ -505,81 +785,366 @@ int GuestSession::directoryQueryInfoInternal(const Utf8Str &strPath, GuestFsObjD
return vrc;
}
+int GuestSession::directoryRemoveFromList(GuestDirectory *pDirectory)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ int rc = VERR_NOT_FOUND;
+
+ SessionDirectories::iterator itDirs = mData.mDirectories.begin();
+ while (itDirs != mData.mDirectories.end())
+ {
+ if (pDirectory == itDirs->second)
+ {
+ /* Make sure to consume the pointer before the one of the
+ * iterator gets released. */
+ ComObjPtr<GuestDirectory> pDir = pDirectory;
+
+ Bstr strName;
+ HRESULT hr = itDirs->second->COMGETTER(DirectoryName)(strName.asOutParam());
+ ComAssertComRC(hr);
+
+ Assert(mData.mDirectories.size());
+ Assert(mData.mNumObjects);
+ 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));
+
+ rc = pDirectory->onRemove();
+ mData.mDirectories.erase(itDirs);
+ mData.mNumObjects--;
+
+ pDir.setNull();
+ break;
+ }
+
+ itDirs++;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int GuestSession::directoryRemoveInternal(const Utf8Str &strPath, uint32_t uFlags,
+ int *pGuestRc)
+{
+ AssertReturn(!(uFlags & ~DIRREMOVE_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ LogFlowThisFunc(("strPath=%s, uFlags=0x%x\n", strPath.c_str(), uFlags));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ GuestWaitEvent *pEvent = NULL;
+ int vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
+ &pEvent);
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /* Prepare HGCM call. */
+ VBOXHGCMSVCPARM paParms[8];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setPointer((void*)strPath.c_str(),
+ (ULONG)strPath.length() + 1);
+ paParms[i++].setUInt32(uFlags);
+
+ alock.release(); /* Drop write lock before sending. */
+
+ vrc = sendCommand(HOST_DIR_REMOVE, i, paParms);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = pEvent->Wait(30 * 1000);
+ if ( vrc == VERR_GSTCTL_GUEST_ERROR
+ && pGuestRc)
+ *pGuestRc = pEvent->GuestResult();
+ }
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
int GuestSession::objectCreateTempInternal(const Utf8Str &strTemplate, const Utf8Str &strPath,
- bool fDirectory, const Utf8Str &strName, int *pGuestRc)
+ bool fDirectory, Utf8Str &strName, int *pGuestRc)
{
+ LogFlowThisFunc(("strTemplate=%s, strPath=%s, fDirectory=%RTbool\n",
+ strTemplate.c_str(), strPath.c_str(), fDirectory));
+
+ int vrc = VINF_SUCCESS;
+
GuestProcessStartupInfo procInfo;
procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_MKTEMP);
procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
- procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
- if (fDirectory)
- procInfo.mArguments.push_back(Utf8Str("-d"));
- if (strPath.length()) /* Otherwise use /tmp or equivalent. */
+
+ try
{
- procInfo.mArguments.push_back(Utf8Str("-t"));
- procInfo.mArguments.push_back(strPath);
+ procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
+ if (fDirectory)
+ procInfo.mArguments.push_back(Utf8Str("-d"));
+ if (strPath.length()) /* Otherwise use /tmp or equivalent. */
+ {
+ procInfo.mArguments.push_back(Utf8Str("-t"));
+ procInfo.mArguments.push_back(strPath);
+ }
+ procInfo.mArguments.push_back(strTemplate);
}
- procInfo.mArguments.push_back(strTemplate);
-
- GuestProcessTool procTool; int guestRc;
- int vrc = procTool.Init(this, procInfo, false /* Async */, &guestRc);
- if (RT_SUCCESS(vrc))
+ catch (std::bad_alloc)
{
- if (RT_SUCCESS(guestRc))
- vrc = procTool.Wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
+ vrc = VERR_NO_MEMORY;
}
+ /** @todo Use an internal HGCM command for this operation, since
+ * we now can run in a user-dedicated session. */
+ int guestRc; GuestCtrlStreamObjects stdOut;
if (RT_SUCCESS(vrc))
+ vrc = GuestProcessTool::RunEx(this, procInfo,
+ &stdOut, 1 /* cStrmOutObjects */,
+ &guestRc);
+ if ( RT_SUCCESS(vrc)
+ && RT_SUCCESS(guestRc))
{
- if (RT_SUCCESS(guestRc))
- guestRc = procTool.TerminatedOk(NULL /* Exit code */);
+ GuestFsObjData objData;
+ if (!stdOut.empty())
+ {
+ vrc = objData.FromMkTemp(stdOut.at(0));
+ if (RT_FAILURE(vrc))
+ {
+ guestRc = vrc;
+ vrc = VERR_GSTCTL_GUEST_ERROR;
+ }
+ }
+ else
+ vrc = VERR_NO_DATA;
+
+ if (RT_SUCCESS(vrc))
+ strName = objData.mName;
}
if (pGuestRc)
*pGuestRc = guestRc;
- if (RT_FAILURE(vrc))
- return vrc;
- return RT_SUCCESS(guestRc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Special guest control rc needed! */
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
}
-int GuestSession::directoryOpenInternal(const Utf8Str &strPath, const Utf8Str &strFilter,
- uint32_t uFlags, ComObjPtr<GuestDirectory> &pDirectory)
+int GuestSession::directoryOpenInternal(const GuestDirectoryOpenInfo &openInfo,
+ ComObjPtr<GuestDirectory> &pDirectory, int *pGuestRc)
{
LogFlowThisFunc(("strPath=%s, strPath=%s, uFlags=%x\n",
- strPath.c_str(), strFilter.c_str(), uFlags));
+ openInfo.mPath.c_str(), openInfo.mFilter.c_str(), openInfo.mFlags));
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ int rc = VERR_MAX_PROCS_REACHED;
+ if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS)
+ return rc;
+
+ /* Create a new (host-based) directory ID and assign it. */
+ uint32_t uNewDirID = 0;
+ ULONG uTries = 0;
+
+ for (;;)
+ {
+ /* Is the directory ID already used? */
+ if (!directoryExists(uNewDirID, NULL /* pDirectory */))
+ {
+ /* 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. */
HRESULT hr = pDirectory.createObject();
if (FAILED(hr))
return VERR_COM_UNEXPECTED;
- int vrc = pDirectory->init(this /* Parent */,
- strPath, strFilter, uFlags);
+ Console *pConsole = mParent->getConsole();
+ AssertPtr(pConsole);
+
+ int vrc = pDirectory->init(pConsole, this /* Parent */,
+ uNewDirID, openInfo);
if (RT_FAILURE(vrc))
return vrc;
- /* Add the created directory to our vector. */
- mData.mDirectories.push_back(pDirectory);
+ /*
+ * 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.mDirectories[uNewDirID] = pDirectory;
+ mData.mNumObjects++;
+ Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
+
+ LogFlowFunc(("Added new guest directory \"%s\" (Session: %RU32) (now total %zu dirs, %RU32 objects)\n",
+ openInfo.mPath.c_str(), mData.mSession.mID, mData.mFiles.size(), mData.mNumObjects));
- LogFlowFunc(("Added new directory \"%s\" (Session: %RU32)\n",
- strPath.c_str(), mData.mId));
+ alock.release(); /* Release lock before firing off event. */
+
+ /** @todo Fire off a VBoxEventType_OnGuestDirectoryRegistered event? */
+ }
+ catch (std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Nothing further to do here yet. */
+ if (pGuestRc)
+ *pGuestRc = VINF_SUCCESS;
+ }
LogFlowFuncLeaveRC(vrc);
return vrc;
}
-int GuestSession::dispatchToProcess(uint32_t uContextID, uint32_t uFunction, void *pvData, size_t cbData)
+int GuestSession::dispatchToDirectory(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
{
- LogFlowFuncEnter();
+ LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
+
+ AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
+
+ if (pSvcCb->mParms < 3)
+ return VERR_INVALID_PARAMETER;
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ uint32_t uDirID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
+#ifdef DEBUG
+ LogFlowFunc(("uDirID=%RU32 (%zu total)\n",
+ uDirID, mData.mFiles.size()));
+#endif
+ int rc;
+ SessionDirectories::const_iterator itDir
+ = mData.mDirectories.find(uDirID);
+ if (itDir != mData.mDirectories.end())
+ {
+ ComObjPtr<GuestDirectory> pDirectory(itDir->second);
+ Assert(!pDirectory.isNull());
+
+ alock.release();
+
+ rc = pDirectory->callbackDispatcher(pCtxCb, pSvcCb);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int GuestSession::dispatchToFile(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
+{
+ LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
+
+ AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ uint32_t uFileID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
+#ifdef DEBUG
+ LogFlowFunc(("uFileID=%RU32 (%zu total)\n",
+ uFileID, mData.mFiles.size()));
+#endif
+ int rc;
+ SessionFiles::const_iterator itFile
+ = mData.mFiles.find(uFileID);
+ if (itFile != mData.mFiles.end())
+ {
+ ComObjPtr<GuestFile> pFile(itFile->second);
+ Assert(!pFile.isNull());
+
+ alock.release();
+
+ rc = pFile->callbackDispatcher(pCtxCb, pSvcCb);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int GuestSession::dispatchToObject(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
+{
+ LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
+
+ AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
+
+ int rc;
+ uint32_t uObjectID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ /* 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! */
+ SessionProcesses::const_iterator itProc = mData.mProcesses.find(uObjectID);
+ if (itProc == mData.mProcesses.end())
+ {
+ SessionFiles::const_iterator itFile = mData.mFiles.find(uObjectID);
+ if (itFile != mData.mFiles.end())
+ {
+ alock.release();
+
+ rc = dispatchToFile(pCtxCb, pSvcCb);
+ }
+ else
+ {
+ SessionDirectories::const_iterator itDir = mData.mDirectories.find(uObjectID);
+ if (itDir != mData.mDirectories.end())
+ {
+ alock.release();
+
+ rc = dispatchToDirectory(pCtxCb, pSvcCb);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ }
+ else
+ {
+ alock.release();
+
+ rc = dispatchToProcess(pCtxCb, pSvcCb);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int GuestSession::dispatchToProcess(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
+{
+ LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
+
+ AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- uint32_t uProcessID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(uContextID);
+ uint32_t uProcessID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
#ifdef DEBUG
- LogFlowFunc(("uProcessID=%RU32 (%RU32 total)\n",
+ LogFlowFunc(("uProcessID=%RU32 (%zu total)\n",
uProcessID, mData.mProcesses.size()));
#endif
int rc;
@@ -587,11 +1152,21 @@ int GuestSession::dispatchToProcess(uint32_t uContextID, uint32_t uFunction, voi
= mData.mProcesses.find(uProcessID);
if (itProc != mData.mProcesses.end())
{
+#ifdef DEBUG_andy
+ ULONG cRefs = itProc->second->AddRef();
+ Assert(cRefs >= 2);
+ LogFlowFunc(("pProcess=%p, cRefs=%RU32\n", &itProc->second, cRefs - 1));
+ itProc->second->Release();
+#endif
ComObjPtr<GuestProcess> pProcess(itProc->second);
Assert(!pProcess.isNull());
+ /* Set protocol version so that pSvcCb can
+ * be interpreted right. */
+ pCtxCb->uProtocol = mData.mProtocolVersion;
+
alock.release();
- rc = pProcess->callbackDispatcher(uContextID, uFunction, pvData, cbData);
+ rc = pProcess->callbackDispatcher(pCtxCb, pSvcCb);
}
else
rc = VERR_NOT_FOUND;
@@ -600,92 +1175,225 @@ int GuestSession::dispatchToProcess(uint32_t uContextID, uint32_t uFunction, voi
return rc;
}
+int GuestSession::dispatchToThis(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
+{
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+#ifdef DEBUG
+ LogFlowThisFunc(("sessionID=%RU32, CID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
+ mData.mSession.mID, pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
+#endif
+
+ int rc;
+ switch (pCbCtx->uFunction)
+ {
+ case GUEST_DISCONNECTED:
+ /** @todo Handle closing all guest objects. */
+ rc = VERR_INTERNAL_ERROR;
+ break;
+
+ case GUEST_SESSION_NOTIFY: /* Guest Additions >= 4.3.0. */
+ {
+ rc = onSessionStatusChange(pCbCtx, pSvcCb);
+ break;
+ }
+
+ default:
+ /* Silently skip unknown callbacks. */
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+inline bool GuestSession::fileExists(uint32_t uFileID, ComObjPtr<GuestFile> *pFile)
+{
+ SessionFiles::const_iterator it = mData.mFiles.find(uFileID);
+ if (it != mData.mFiles.end())
+ {
+ if (pFile)
+ *pFile = it->second;
+ return true;
+ }
+ return false;
+}
+
int GuestSession::fileRemoveFromList(GuestFile *pFile)
{
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- for (SessionFiles::iterator itFiles = mData.mFiles.begin();
- itFiles != mData.mFiles.end(); ++itFiles)
+ int rc = VERR_NOT_FOUND;
+
+ SessionFiles::iterator itFiles = mData.mFiles.begin();
+ while (itFiles != mData.mFiles.end())
{
- if (pFile == (*itFiles))
+ if (pFile == itFiles->second)
{
+ /* Make sure to consume the pointer before the one of thfe
+ * iterator gets released. */
+ ComObjPtr<GuestFile> pCurFile = pFile;
+
Bstr strName;
- HRESULT hr = (*itFiles)->COMGETTER(FileName)(strName.asOutParam());
+ HRESULT hr = pCurFile->COMGETTER(FileName)(strName.asOutParam());
ComAssertComRC(hr);
Assert(mData.mNumObjects);
- LogFlowThisFunc(("Removing file \"%s\" (Session: %RU32) (now total %ld files, %ld objects)\n",
- Utf8Str(strName).c_str(), mData.mId, mData.mFiles.size() - 1, mData.mNumObjects - 1));
-#ifdef DEBUG
- ULONG cRefs = pFile->AddRef();
- LogFlowThisFunc(("pObject=%p, cRefs=%RU32\n", pFile, cRefs));
- pFile->Release();
-#endif
+ LogFlowThisFunc(("Removing guest file \"%s\" (Session: %RU32) (now total %zu files, %RU32 objects)\n",
+ Utf8Str(strName).c_str(), mData.mSession.mID, mData.mFiles.size() - 1, mData.mNumObjects - 1));
+
+ rc = pFile->onRemove();
mData.mFiles.erase(itFiles);
mData.mNumObjects--;
- return VINF_SUCCESS;
+
+ alock.release(); /* Release lock before firing off event. */
+
+ fireGuestFileRegisteredEvent(mEventSource, this, pCurFile,
+ false /* Unregistered */);
+ pCurFile.setNull();
+ break;
}
+
+ itFiles++;
}
- return VERR_NOT_FOUND;
+ LogFlowFuncLeaveRC(rc);
+ return rc;
}
int GuestSession::fileRemoveInternal(const Utf8Str &strPath, int *pGuestRc)
{
+ LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
+
+ int vrc = VINF_SUCCESS;
+
GuestProcessStartupInfo procInfo;
GuestProcessStream streamOut;
procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_RM);
procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
- procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
- procInfo.mArguments.push_back(strPath); /* The file we want to remove. */
-
- GuestProcessTool procTool; int guestRc;
- int vrc = procTool.Init(this, procInfo, false /* Async */, &guestRc);
- if (RT_SUCCESS(vrc))
- vrc = procTool.Wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
- if (RT_SUCCESS(vrc))
+ try
+ {
+ procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
+ procInfo.mArguments.push_back(strPath); /* The file we want to remove. */
+ }
+ catch (std::bad_alloc)
{
- if (RT_SUCCESS(guestRc))
- guestRc = procTool.TerminatedOk(NULL /* Exit code */);
+ vrc = VERR_NO_MEMORY;
}
- if (pGuestRc)
- *pGuestRc = guestRc;
+ if (RT_SUCCESS(vrc))
+ vrc = GuestProcessTool::Run(this, procInfo, pGuestRc);
- if (RT_FAILURE(vrc))
- return vrc;
- return RT_SUCCESS(guestRc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Special guest control rc needed! */
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
}
-int GuestSession::fileOpenInternal(const Utf8Str &strPath, const Utf8Str &strOpenMode, const Utf8Str &strDisposition,
- uint32_t uCreationMode, int64_t iOffset, ComObjPtr<GuestFile> &pFile, int *pGuestRc)
+int GuestSession::fileOpenInternal(const GuestFileOpenInfo &openInfo,
+ ComObjPtr<GuestFile> &pFile, int *pGuestRc)
{
- LogFlowThisFunc(("strPath=%s, strOpenMode=%s, strDisposition=%s, uCreationMode=%x, iOffset=%RI64\n",
- strPath.c_str(), strOpenMode.c_str(), strDisposition.c_str(), uCreationMode, iOffset));
+ LogFlowThisFunc(("strPath=%s, strOpenMode=%s, strDisposition=%s, uCreationMode=%x, uOffset=%RU64\n",
+ openInfo.mFileName.c_str(), openInfo.mOpenMode.c_str(), openInfo.mDisposition.c_str(),
+ openInfo.mCreationMode, openInfo.mInitialOffset));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ /* Guest Additions < 4.3 don't support handling
+ guest files, skip. */
+ if (mData.mProtocolVersion < 2)
+ {
+ LogFlowThisFunc(("Installed Guest Additions don't support handling guest files, skipping\n"));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ int rc = VERR_MAX_PROCS_REACHED;
+ if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS)
+ return rc;
+
+ /* Create a new (host-based) file ID and assign it. */
+ uint32_t uNewFileID = 0;
+ ULONG uTries = 0;
+
+ for (;;)
+ {
+ /* Is the file ID already used? */
+ if (!fileExists(uNewFileID, NULL /* pFile */))
+ {
+ /* 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. */
HRESULT hr = pFile.createObject();
if (FAILED(hr))
return VERR_COM_UNEXPECTED;
- int vrc = pFile->init(this /* Parent */,
- strPath, strOpenMode, strDisposition, uCreationMode, iOffset, pGuestRc);
- if (RT_FAILURE(vrc))
- return vrc;
- /** @todo Handle guestRc. */
+ Console *pConsole = mParent->getConsole();
+ AssertPtr(pConsole);
- /* Add the created directory to our vector. */
- mData.mFiles.push_back(pFile);
- mData.mNumObjects++;
- Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
+ rc = pFile->init(pConsole, this /* GuestSession */,
+ uNewFileID, openInfo);
+ if (RT_FAILURE(rc))
+ return rc;
- LogFlowFunc(("Added new file \"%s\" (Session: %RU32) (now total %ld files, %ld objects)\n",
- strPath.c_str(), mData.mId, mData.mProcesses.size(), mData.mNumObjects));
+ /*
+ * 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.mFiles[uNewFileID] = pFile;
+ mData.mNumObjects++;
+ Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
- LogFlowFuncLeaveRC(vrc);
- return vrc;
+ LogFlowFunc(("Added new guest file \"%s\" (Session: %RU32) (now total %zu files, %RU32 objects)\n",
+ openInfo.mFileName.c_str(), mData.mSession.mID, mData.mFiles.size(), mData.mNumObjects));
+
+ alock.release(); /* Release lock before firing off event. */
+
+ fireGuestFileRegisteredEvent(mEventSource, this, pFile,
+ true /* Registered */);
+ }
+ catch (std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ int guestRc;
+ rc = pFile->openFile(30 * 1000 /* 30s timeout */, &guestRc);
+ if ( rc == VERR_GSTCTL_GUEST_ERROR
+ && pGuestRc)
+ {
+ *pGuestRc = guestRc;
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
}
int GuestSession::fileQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc)
@@ -719,39 +1427,45 @@ int GuestSession::fsQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &ob
{
LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
+ int vrc = VINF_SUCCESS;
+
/** @todo Merge this with IGuestFile::queryInfo(). */
GuestProcessStartupInfo procInfo;
procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_STAT);
procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
- /* Construct arguments. */
- procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
- procInfo.mArguments.push_back(strPath);
+ try
+ {
+ /* Construct arguments. */
+ procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
+ procInfo.mArguments.push_back(strPath);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
- GuestProcessTool procTool; int guestRc;
- int vrc = procTool.Init(this, procInfo, false /* Async */, &guestRc);
- if (RT_SUCCESS(vrc))
- vrc = procTool.Wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
+ int guestRc; GuestCtrlStreamObjects stdOut;
if (RT_SUCCESS(vrc))
+ vrc = GuestProcessTool::RunEx(this, procInfo,
+ &stdOut, 1 /* cStrmOutObjects */,
+ &guestRc);
+ if ( RT_SUCCESS(vrc)
+ && RT_SUCCESS(guestRc))
{
- guestRc = procTool.TerminatedOk(NULL /* Exit code */);
- if (RT_SUCCESS(guestRc))
- {
- GuestProcessStreamBlock curBlock;
- vrc = procTool.GetCurrentBlock(OUTPUT_HANDLE_ID_STDOUT, curBlock);
- /** @todo Check for more / validate blocks! */
- if (RT_SUCCESS(vrc))
- vrc = objData.FromStat(curBlock);
- }
+ if (!stdOut.empty())
+ vrc = objData.FromStat(stdOut.at(0));
+ else
+ vrc = VERR_NO_DATA;
}
- if (pGuestRc)
+ if ( vrc == VERR_GSTCTL_GUEST_ERROR
+ && pGuestRc)
*pGuestRc = guestRc;
- LogFlowFuncLeaveRC(vrc);
- if (RT_FAILURE(vrc))
- return vrc;
- return RT_SUCCESS(guestRc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Special guest control rc needed! */
+ LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n",
+ vrc, guestRc));
+ return vrc;
}
const GuestCredentials& GuestSession::getCredentials(void)
@@ -766,43 +1480,413 @@ const GuestEnvironment& GuestSession::getEnvironment(void)
Utf8Str GuestSession::getName(void)
{
- return mData.mName;
+ return mData.mSession.mName;
}
-int GuestSession::processRemoveFromList(GuestProcess *pProcess)
+/* static */
+Utf8Str GuestSession::guestErrorToString(int guestRc)
+{
+ Utf8Str strError;
+
+ /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
+ switch (guestRc)
+ {
+ case VERR_INVALID_VM_HANDLE:
+ strError += Utf8StrFmt(tr("VMM device is not available (is the VM running?)"));
+ break;
+
+ case VERR_HGCM_SERVICE_NOT_FOUND:
+ strError += Utf8StrFmt(tr("The guest execution service is not available"));
+ break;
+
+ case VERR_AUTHENTICATION_FAILURE:
+ strError += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
+ break;
+
+ case VERR_TIMEOUT:
+ strError += Utf8StrFmt(tr("The guest did not respond within time"));
+ break;
+
+ case VERR_CANCELLED:
+ strError += Utf8StrFmt(tr("The session operation was canceled"));
+ break;
+
+ case VERR_PERMISSION_DENIED:
+ strError += Utf8StrFmt(tr("Invalid user/password credentials"));
+ break;
+
+ case VERR_MAX_PROCS_REACHED:
+ strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached"));
+ break;
+
+ case VERR_NOT_EQUAL: /** @todo Imprecise to the user; can mean anything and all. */
+ strError += Utf8StrFmt(tr("Unable to retrieve requested information"));
+ break;
+
+ case VERR_NOT_FOUND:
+ strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)"));
+ break;
+
+ default:
+ strError += Utf8StrFmt("%Rrc", guestRc);
+ 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.
+ */
+HRESULT GuestSession::isReadyExternal(void)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ /** @todo Be a bit more informative. */
+ if (mData.mStatus != GuestSessionStatus_Started)
+ return setError(E_UNEXPECTED, tr("Session is not in started state"));
+
+ return S_OK;
+}
+
+/**
+ * Called by IGuest right before this session gets removed from
+ * the public session list.
+ */
+int GuestSession::onRemove(void)
{
LogFlowThisFuncEnter();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ 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())
+ {
+ mEventSource->UnregisterListener(mLocalListener);
+
+ mLocalListener.setNull();
+ unconst(mEventSource).setNull();
+ }
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+/** No locking! */
+int GuestSession::onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
+{
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ /* pCallback is optional. */
+ AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
+
+ if (pSvcCbData->mParms < 3)
+ return VERR_INVALID_PARAMETER;
+
+ CALLBACKDATA_SESSION_NOTIFY dataCb;
+ /* pSvcCb->mpaParms[0] always contains the context ID. */
+ int vrc = pSvcCbData->mpaParms[1].getUInt32(&dataCb.uType);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCbData->mpaParms[2].getUInt32(&dataCb.uResult);
+ AssertRCReturn(vrc, vrc);
+
+ LogFlowThisFunc(("ID=%RU32, uType=%RU32, guestRc=%Rrc\n",
+ mData.mSession.mID, dataCb.uType, dataCb.uResult));
+
+ GuestSessionStatus_T sessionStatus = GuestSessionStatus_Undefined;
+
+ int guestRc = dataCb.uResult; /** @todo uint32_t vs. int. */
+ switch (dataCb.uType)
+ {
+ case GUEST_SESSION_NOTIFYTYPE_ERROR:
+ sessionStatus = GuestSessionStatus_Error;
+ break;
+
+ case GUEST_SESSION_NOTIFYTYPE_STARTED:
+ sessionStatus = GuestSessionStatus_Started;
+ break;
+
+ case GUEST_SESSION_NOTIFYTYPE_TEN:
+ case GUEST_SESSION_NOTIFYTYPE_TES:
+ case GUEST_SESSION_NOTIFYTYPE_TEA:
+ sessionStatus = GuestSessionStatus_Terminated;
+ break;
+
+ case GUEST_SESSION_NOTIFYTYPE_TOK:
+ sessionStatus = GuestSessionStatus_TimedOutKilled;
+ break;
+
+ case GUEST_SESSION_NOTIFYTYPE_TOA:
+ sessionStatus = GuestSessionStatus_TimedOutAbnormally;
+ break;
+
+ case GUEST_SESSION_NOTIFYTYPE_DWN:
+ sessionStatus = GuestSessionStatus_Down;
+ break;
+
+ case GUEST_SESSION_NOTIFYTYPE_UNDEFINED:
+ default:
+ vrc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ if (RT_SUCCESS(vrc))
+ {
+ if (RT_FAILURE(guestRc))
+ sessionStatus = GuestSessionStatus_Error;
+ }
+
+ /* Set the session status. */
+ if (RT_SUCCESS(vrc))
+ vrc = setSessionStatus(sessionStatus, guestRc);
+
+ LogFlowThisFunc(("ID=%RU32, guestRc=%Rrc\n", mData.mSession.mID, guestRc));
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestSession::startSessionInternal(int *pGuestRc)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
+ mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion,
+ mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
+
+ /* Guest Additions < 4.3 don't support opening dedicated
+ guest sessions. Simply return success here. */
+ if (mData.mProtocolVersion < 2)
+ {
+ mData.mStatus = GuestSessionStatus_Started;
+
+ LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n"));
+ return VINF_SUCCESS;
+ }
+
+ if (mData.mStatus != GuestSessionStatus_Undefined)
+ return VINF_SUCCESS;
+
+ /** @todo mData.mSession.uFlags validation. */
+
+ /* Set current session status. */
+ mData.mStatus = GuestSessionStatus_Starting;
+ mData.mRC = VINF_SUCCESS; /* Clear previous error, if any. */
+
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
+ try
+ {
+ eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
+
+ vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
+ eventTypes, &pEvent);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ VBOXHGCMSVCPARM paParms[8];
+
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setUInt32(mData.mProtocolVersion);
+ paParms[i++].setPointer((void*)mData.mCredentials.mUser.c_str(),
+ (ULONG)mData.mCredentials.mUser.length() + 1);
+ paParms[i++].setPointer((void*)mData.mCredentials.mPassword.c_str(),
+ (ULONG)mData.mCredentials.mPassword.length() + 1);
+ paParms[i++].setPointer((void*)mData.mCredentials.mDomain.c_str(),
+ (ULONG)mData.mCredentials.mDomain.length() + 1);
+ paParms[i++].setUInt32(mData.mSession.mOpenFlags);
+
+ alock.release(); /* Drop write lock before sending. */
+
+ vrc = sendCommand(HOST_SESSION_CREATE, i, paParms);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = waitForStatusChange(pEvent, GuestSessionWaitForFlag_Start,
+ 30 * 1000 /* 30s timeout */,
+ NULL /* Session status */, pGuestRc);
+ }
+ 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.
+ */
+ mData.mStatus = GuestSessionStatus_Error;
+ mData.mRC = vrc;
+ }
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestSession::startSessionAsync(void)
+{
+ LogFlowThisFuncEnter();
+
+ int vrc;
+
+ try
+ {
+ /* Asynchronously open the session on the guest by kicking off a
+ * worker thread. */
+ std::auto_ptr<GuestSessionTaskInternalOpen> pTask(new GuestSessionTaskInternalOpen(this));
+ AssertReturn(pTask->isOk(), pTask->rc());
+
+ vrc = RTThreadCreate(NULL, GuestSession::startSessionThread,
+ (void *)pTask.get(), 0,
+ RTTHREADTYPE_MAIN_WORKER, 0,
+ "gctlSesStart");
+ if (RT_SUCCESS(vrc))
+ {
+ /* pTask is now owned by openSessionThread(), so release it. */
+ pTask.release();
+ }
+ }
+ catch(std::bad_alloc &)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+/* static */
+DECLCALLBACK(int) GuestSession::startSessionThread(RTTHREAD Thread, void *pvUser)
+{
+ LogFlowFunc(("pvUser=%p\n", pvUser));
+
+ std::auto_ptr<GuestSessionTaskInternalOpen> pTask(static_cast<GuestSessionTaskInternalOpen*>(pvUser));
+ AssertPtr(pTask.get());
+
+ const ComObjPtr<GuestSession> pSession(pTask->Session());
+ Assert(!pSession.isNull());
+
+ AutoCaller autoCaller(pSession);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ int vrc = pSession->startSessionInternal(NULL /* Guest rc, ignored */);
+ /* Nothing to do here anymore. */
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestSession::pathRenameInternal(const Utf8Str &strSource, const Utf8Str &strDest,
+ uint32_t uFlags, int *pGuestRc)
+{
+ AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
+ strSource.c_str(), strDest.c_str(), uFlags));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ GuestWaitEvent *pEvent = NULL;
+ int vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
+ &pEvent);
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /* Prepare HGCM call. */
+ VBOXHGCMSVCPARM paParms[8];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setPointer((void*)strSource.c_str(),
+ (ULONG)strSource.length() + 1);
+ paParms[i++].setPointer((void*)strDest.c_str(),
+ (ULONG)strDest.length() + 1);
+ paParms[i++].setUInt32(uFlags);
+
+ alock.release(); /* Drop write lock before sending. */
+
+ vrc = sendCommand(HOST_PATH_RENAME, i, paParms);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = pEvent->Wait(30 * 1000);
+ if ( vrc == VERR_GSTCTL_GUEST_ERROR
+ && pGuestRc)
+ *pGuestRc = pEvent->GuestResult();
+ }
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestSession::processRemoveFromList(GuestProcess *pProcess)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+
+ LogFlowThisFunc(("pProcess=%p\n", pProcess));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
int rc = VERR_NOT_FOUND;
ULONG uPID;
HRESULT hr = pProcess->COMGETTER(PID)(&uPID);
+ ComAssertComRC(hr);
- LogFlowFunc(("Closing process (PID=%RU32) ...\n", uPID));
+ LogFlowFunc(("Removing process (PID=%RU32) ...\n", uPID));
- for (SessionProcesses::iterator itProcs = mData.mProcesses.begin();
- itProcs != mData.mProcesses.end(); ++itProcs)
+ SessionProcesses::iterator itProcs = mData.mProcesses.begin();
+ while (itProcs != mData.mProcesses.end())
{
if (pProcess == itProcs->second)
{
- GuestProcess *pCurProc = itProcs->second;
- AssertPtr(pCurProc);
+#ifdef DEBUG_andy
+ ULONG cRefs = pProcess->AddRef();
+ Assert(cRefs >= 2);
+ LogFlowFunc(("pProcess=%p, cRefs=%RU32\n", pProcess, cRefs - 1));
+ pProcess->Release();
+#endif
+ /* Make sure to consume the pointer before the one of the
+ * iterator gets released. */
+ ComObjPtr<GuestProcess> pProc = pProcess;
- hr = pCurProc->COMGETTER(PID)(&uPID);
+ hr = pProc->COMGETTER(PID)(&uPID);
ComAssertComRC(hr);
+ Assert(mData.mProcesses.size());
Assert(mData.mNumObjects);
- LogFlowFunc(("Removing process (Session: %RU32) with process ID=%RU32, guest PID=%RU32 (now total %ld processes, %ld objects)\n",
- mData.mId, pCurProc->getProcessID(), uPID, mData.mProcesses.size() - 1, mData.mNumObjects - 1));
+ 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));
+ rc = pProcess->onRemove();
mData.mProcesses.erase(itProcs);
mData.mNumObjects--;
- rc = VINF_SUCCESS;
+ alock.release(); /* Release lock before firing off event. */
+
+ fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc,
+ uPID, false /* Process unregistered */);
+ pProc.setNull();
break;
}
+
+ itProcs++;
}
LogFlowFuncLeaveRC(rc);
@@ -849,6 +1933,15 @@ int GuestSession::processCreateExInteral(GuestProcessStartupInfo &procInfo, ComO
}
}
+ if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
+ && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
+ || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
+ )
+ )
+ {
+ return VERR_INVALID_PARAMETER;
+ }
+
/* Adjust timeout. If set to 0, we define
* an infinite timeout. */
if (procInfo.mTimeoutMS == 0)
@@ -869,7 +1962,7 @@ int GuestSession::processCreateExInteral(GuestProcessStartupInfo &procInfo, ComO
for (;;)
{
/* Is the context ID already used? */
- if (!processExists(uNewProcessID, NULL /* pProgress */))
+ if (!processExists(uNewProcessID, NULL /* pProcess */))
{
/* Callback with context ID was not found. This means
* we can use this context ID for our new callback we want
@@ -881,7 +1974,7 @@ int GuestSession::processCreateExInteral(GuestProcessStartupInfo &procInfo, ComO
if (uNewProcessID == VBOX_GUESTCTRL_MAX_OBJECTS)
uNewProcessID = 0;
- if (++uTries == UINT32_MAX)
+ if (++uTries == VBOX_GUESTCTRL_MAX_OBJECTS)
break; /* Don't try too hard. */
}
@@ -893,18 +1986,30 @@ int GuestSession::processCreateExInteral(GuestProcessStartupInfo &procInfo, ComO
if (FAILED(hr))
return VERR_COM_UNEXPECTED;
- rc = pProcess->init(mData.mParent->getConsole() /* Console */, this /* Session */,
+ rc = pProcess->init(mParent->getConsole() /* Console */, this /* Session */,
uNewProcessID, procInfo);
if (RT_FAILURE(rc))
return rc;
/* Add the created process to our map. */
- mData.mProcesses[uNewProcessID] = pProcess;
- mData.mNumObjects++;
- Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
+ try
+ {
+ mData.mProcesses[uNewProcessID] = pProcess;
+ mData.mNumObjects++;
+ Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
+
+ LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes, %RU32 objects)\n",
+ mData.mSession.mID, uNewProcessID, mData.mProcesses.size(), mData.mNumObjects));
- LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %ld processes, %ld objects)\n",
- mData.mId, uNewProcessID, mData.mProcesses.size(), mData.mNumObjects));
+ alock.release(); /* Release lock before firing off event. */
+
+ fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess,
+ 0 /* PID */, true /* Process registered */);
+ }
+ catch (std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
return rc;
}
@@ -949,6 +2054,92 @@ inline int GuestSession::processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pP
return VERR_NOT_FOUND;
}
+int GuestSession::sendCommand(uint32_t uFunction,
+ uint32_t uParms, PVBOXHGCMSVCPARM paParms)
+{
+ LogFlowThisFuncEnter();
+
+#ifndef VBOX_GUESTCTRL_TEST_CASE
+ ComObjPtr<Console> pConsole = mParent->getConsole();
+ Assert(!pConsole.isNull());
+
+ /* Forward the information to the VMM device. */
+ VMMDev *pVMMDev = pConsole->getVMMDev();
+ AssertPtr(pVMMDev);
+
+ LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
+ int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
+ if (RT_FAILURE(vrc))
+ {
+ /** @todo What to do here? */
+ }
+#else
+ /* Not needed within testcases. */
+ int vrc = VINF_SUCCESS;
+#endif
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+/* static */
+HRESULT GuestSession::setErrorExternal(VirtualBoxBase *pInterface, int guestRc)
+{
+ AssertPtr(pInterface);
+ AssertMsg(RT_FAILURE(guestRc), ("Guest rc does not indicate a failure when setting error\n"));
+
+ return pInterface->setError(VBOX_E_IPRT_ERROR, GuestSession::guestErrorToString(guestRc).c_str());
+}
+
+/* Does not do locking; caller is responsible for that! */
+int GuestSession::setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
+{
+ LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
+ mData.mStatus, sessionStatus, sessionRc));
+
+ if (sessionStatus == GuestSessionStatus_Error)
+ {
+ AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
+ /* Do not allow overwriting an already set error. If this happens
+ * this means we forgot some error checking/locking somewhere. */
+ AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
+ }
+ else
+ AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
+
+ if (mData.mStatus != sessionStatus)
+ {
+ mData.mStatus = sessionStatus;
+ mData.mRC = sessionRc;
+
+ ComObjPtr<VirtualBoxErrorInfo> errorInfo;
+ HRESULT hr = errorInfo.createObject();
+ ComAssertComRC(hr);
+ int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
+ COM_IIDOF(IGuestSession), getComponentName(),
+ guestErrorToString(sessionRc));
+ AssertRC(rc2);
+
+ fireGuestSessionStateChangedEvent(mEventSource, this,
+ mData.mSession.mID, sessionStatus, errorInfo);
+ }
+
+ return VINF_SUCCESS;
+}
+
+int GuestSession::signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= 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);*/
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
int GuestSession::startTaskAsync(const Utf8Str &strTaskDesc,
GuestSessionTask *pTask, ComObjPtr<Progress> &pProgress)
{
@@ -990,33 +2181,252 @@ int GuestSession::startTaskAsync(const Utf8Str &strTaskDesc,
*/
int GuestSession::queryInfo(void)
{
-#if 1
- /* Since the new functions were not implemented yet, force Main to use protocol ver 1. */
- mData.mProtocolVersion = 1;
-#else
/*
* Try querying the guest control protocol version running on the guest.
* This is done using the Guest Additions version
*/
- ComObjPtr<Guest> pGuest = mData.mParent;
+ ComObjPtr<Guest> pGuest = mParent;
Assert(!pGuest.isNull());
uint32_t uVerAdditions = pGuest->getAdditionsVersion();
- mData.mProtocolVersion = ( VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions) >= 4
- && VBOX_FULL_VERSION_GET_MINOR(uVerAdditions) >= 2) /** @todo What's about v5.0 ? */
+ uint32_t uVBoxMajor = VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions);
+ uint32_t uVBoxMinor = VBOX_FULL_VERSION_GET_MINOR(uVerAdditions);
+
+#ifdef DEBUG_andy
+ /* Hardcode the to-used protocol version; nice for testing side effects. */
+ mData.mProtocolVersion = 2;
+#else
+ mData.mProtocolVersion = (
+ /* VBox 5.0 and up. */
+ uVBoxMajor >= 5
+ /* VBox 4.3 and up. */
+ || (uVBoxMajor == 4 && uVBoxMinor >= 3))
? 2 /* Guest control 2.0. */
- : 1; /* Legacy guest control (VBox < 4.2). */
+ : 1; /* Legacy guest control (VBox < 4.3). */
/* Build revision is ignored. */
+#endif
+
+ LogFlowThisFunc(("uVerAdditions=%RU32 (%RU32.%RU32), mProtocolVersion=%RU32\n",
+ uVerAdditions, uVBoxMajor, uVBoxMinor, mData.mProtocolVersion));
/* Tell the user but don't bitch too often. */
static short s_gctrlLegacyWarning = 0;
- if (s_gctrlLegacyWarning++ < 3) /** @todo Find a bit nicer text. */
+ if ( mData.mProtocolVersion < 2
+ && s_gctrlLegacyWarning++ < 3) /** @todo Find a bit nicer text. */
LogRel((tr("Warning: Guest Additions are older (%ld.%ld) than host capabilities for guest control, please upgrade them. Using protocol version %ld now\n"),
- VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions), VBOX_FULL_VERSION_GET_MINOR(uVerAdditions), mData.mProtocolVersion));
-#endif
+ uVBoxMajor, uVBoxMinor, mData.mProtocolVersion));
+
return VINF_SUCCESS;
}
+int GuestSession::waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *pGuestRc)
+{
+ LogFlowThisFuncEnter();
+
+ AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
+
+ /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n",
+ fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));*/
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ /* Did some error occur before? Then skip waiting and return. */
+ if (mData.mStatus == GuestSessionStatus_Error)
+ {
+ waitResult = GuestSessionWaitResult_Error;
+ AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
+ if (pGuestRc)
+ *pGuestRc = mData.mRC; /* Return last set error. */
+ return VERR_GSTCTL_GUEST_ERROR;
+ }
+
+ /* Guest Additions < 4.3 don't support session handling, skip. */
+ if (mData.mProtocolVersion < 2)
+ {
+ waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
+
+ LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
+ return VINF_SUCCESS;
+ }
+
+ waitResult = GuestSessionWaitResult_None;
+ if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
+ {
+ switch (mData.mStatus)
+ {
+ case GuestSessionStatus_Terminated:
+ case GuestSessionStatus_Down:
+ waitResult = GuestSessionWaitResult_Terminate;
+ break;
+
+ case GuestSessionStatus_TimedOutKilled:
+ case GuestSessionStatus_TimedOutAbnormally:
+ waitResult = GuestSessionWaitResult_Timeout;
+ break;
+
+ case GuestSessionStatus_Error:
+ /* Handled above. */
+ break;
+
+ case GuestSessionStatus_Started:
+ waitResult = GuestSessionWaitResult_Start;
+ break;
+
+ case GuestSessionStatus_Undefined:
+ case GuestSessionStatus_Starting:
+ /* Do the waiting below. */
+ break;
+
+ default:
+ AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
+ return VERR_NOT_IMPLEMENTED;
+ }
+ }
+ else if (fWaitFlags & GuestSessionWaitForFlag_Start)
+ {
+ switch (mData.mStatus)
+ {
+ case GuestSessionStatus_Started:
+ case GuestSessionStatus_Terminating:
+ case GuestSessionStatus_Terminated:
+ case GuestSessionStatus_Down:
+ waitResult = GuestSessionWaitResult_Start;
+ break;
+
+ case GuestSessionStatus_Error:
+ waitResult = GuestSessionWaitResult_Error;
+ break;
+
+ case GuestSessionStatus_TimedOutKilled:
+ case GuestSessionStatus_TimedOutAbnormally:
+ waitResult = GuestSessionWaitResult_Timeout;
+ break;
+
+ case GuestSessionStatus_Undefined:
+ case GuestSessionStatus_Starting:
+ /* Do the waiting below. */
+ break;
+
+ default:
+ AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
+ return VERR_NOT_IMPLEMENTED;
+ }
+ }
+
+ LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
+ mData.mStatus, mData.mRC, waitResult));
+
+ /* No waiting needed? Return immediately using the last set error. */
+ if (waitResult != GuestSessionWaitResult_None)
+ {
+ if (pGuestRc)
+ *pGuestRc = mData.mRC; /* Return last set error (if any). */
+ return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
+ }
+
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
+ try
+ {
+ eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
+
+ vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
+ eventTypes, &pEvent);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ alock.release(); /* Release lock before waiting. */
+
+ GuestSessionStatus_T sessionStatus;
+ vrc = waitForStatusChange(pEvent, fWaitFlags,
+ uTimeoutMS, &sessionStatus, pGuestRc);
+ if (RT_SUCCESS(vrc))
+ {
+ switch (sessionStatus)
+ {
+ case GuestSessionStatus_Started:
+ waitResult = GuestSessionWaitResult_Start;
+ break;
+
+ case GuestSessionStatus_Terminated:
+ waitResult = GuestSessionWaitResult_Terminate;
+ break;
+
+ case GuestSessionStatus_TimedOutKilled:
+ case GuestSessionStatus_TimedOutAbnormally:
+ waitResult = GuestSessionWaitResult_Timeout;
+ break;
+
+ case GuestSessionStatus_Down:
+ waitResult = GuestSessionWaitResult_Terminate;
+ break;
+
+ case GuestSessionStatus_Error:
+ waitResult = GuestSessionWaitResult_Error;
+ break;
+
+ default:
+ waitResult = GuestSessionWaitResult_Status;
+ break;
+ }
+ }
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestSession::waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
+ GuestSessionStatus_T *pSessionStatus, int *pGuestRc)
+{
+ AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+
+ VBoxEventType_T evtType;
+ ComPtr<IEvent> pIEvent;
+ int vrc = waitForEvent(pEvent, uTimeoutMS,
+ &evtType, pIEvent.asOutParam());
+ if (RT_SUCCESS(vrc))
+ {
+ Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
+
+ ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
+ Assert(!pChangedEvent.isNull());
+
+ GuestSessionStatus_T sessionStatus;
+ pChangedEvent->COMGETTER(Status)(&sessionStatus);
+ if (pSessionStatus)
+ *pSessionStatus = sessionStatus;
+
+ ComPtr<IVirtualBoxErrorInfo> errorInfo;
+ HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
+ ComAssertComRC(hr);
+
+ LONG lGuestRc;
+ hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
+ ComAssertComRC(hr);
+ if (RT_FAILURE((int)lGuestRc))
+ vrc = VERR_GSTCTL_GUEST_ERROR;
+ if (pGuestRc)
+ *pGuestRc = (int)lGuestRc;
+
+ LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
+ mData.mSession.mID, sessionStatus,
+ RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
+ }
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
// implementation of public methods
/////////////////////////////////////////////////////////////////////////////
@@ -1030,17 +2440,32 @@ STDMETHODIMP GuestSession::Close(void)
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
+ /* Close session on guest. */
+ int guestRc = VINF_SUCCESS;
+ int rc = closeSession(0 /* Flags */, 30 * 1000 /* Timeout */,
+ &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. */
- mData.mParent->sessionRemove(this);
+ int rc2 = mParent->sessionRemove(this);
+ if (rc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
+ rc2 = VINF_SUCCESS;
- /*
- * Release autocaller before calling uninit.
- */
- autoCaller.release();
+ if (RT_SUCCESS(rc))
+ rc = rc2;
- uninit();
+ LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n",
+ rc, guestRc));
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_GSTCTL_GUEST_ERROR)
+ return GuestSession::setErrorExternal(this, guestRc);
+
+ return setError(VBOX_E_IPRT_ERROR,
+ tr("Closing guest session failed with %Rrc"), rc);
+ }
- LogFlowFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -1137,7 +2562,7 @@ STDMETHODIMP GuestSession::CopyTo(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn
ComObjPtr<Progress> pProgress;
SessionTaskCopyTo *pTask = new SessionTaskCopyTo(this /* GuestSession */,
Utf8Str(aSource), Utf8Str(aDest), fFlags);
- AssertPtrReturn(pTask, VERR_NO_MEMORY);
+ AssertPtrReturn(pTask, E_OUTOFMEMORY);
int rc = startTaskAsync(Utf8StrFmt(tr("Copying \"%ls\" from host to \"%ls\" on the guest"), aSource, aDest),
pTask, pProgress);
if (RT_SUCCESS(rc))
@@ -1194,8 +2619,9 @@ STDMETHODIMP GuestSession::DirectoryCreate(IN_BSTR aPath, ULONG aMode,
{
switch (rc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
- hr = GuestProcess::setErrorExternal(this, guestRc);
+ case VERR_GSTCTL_GUEST_ERROR:
+ /** @todo Handle VERR_NOT_EQUAL (meaning process exit code <> 0). */
+ hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Could not create directory"));
break;
case VERR_INVALID_PARAMETER:
@@ -1206,10 +2632,6 @@ STDMETHODIMP GuestSession::DirectoryCreate(IN_BSTR aPath, ULONG aMode,
hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Unexpectedly aborted"));
break;
- case VERR_CANT_CREATE:
- hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Could not create directory"));
- break;
-
default:
hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %Rrc"), rc);
break;
@@ -1250,7 +2672,7 @@ STDMETHODIMP GuestSession::DirectoryCreateTemp(IN_BSTR aTemplate, ULONG aMode, I
{
switch (rc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
hr = GuestProcess::setErrorExternal(this, guestRc);
break;
@@ -1291,7 +2713,7 @@ STDMETHODIMP GuestSession::DirectoryExists(IN_BSTR aPath, BOOL *aExists)
{
switch (rc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
hr = GuestProcess::setErrorExternal(this, guestRc);
break;
@@ -1335,8 +2757,13 @@ STDMETHODIMP GuestSession::DirectoryOpen(IN_BSTR aPath, IN_BSTR aFilter, ComSafe
HRESULT hr = S_OK;
- ComObjPtr <GuestDirectory> pDirectory;
- int rc = directoryOpenInternal(Utf8Str(aPath), Utf8Str(aFilter), fFlags, pDirectory);
+ GuestDirectoryOpenInfo openInfo;
+ openInfo.mPath = Utf8Str(aPath);
+ openInfo.mFilter = Utf8Str(aFilter);
+ openInfo.mFlags = fFlags;
+
+ ComObjPtr <GuestDirectory> pDirectory; int guestRc;
+ int rc = directoryOpenInternal(openInfo, pDirectory, &guestRc);
if (RT_SUCCESS(rc))
{
/* Return directory object to the caller. */
@@ -1351,6 +2778,10 @@ STDMETHODIMP GuestSession::DirectoryOpen(IN_BSTR aPath, IN_BSTR aFilter, ComSafe
Utf8Str(aPath).c_str()));
break;
+ case VERR_GSTCTL_GUEST_ERROR:
+ hr = GuestDirectory::setErrorExternal(this, guestRc);
+ break;
+
default:
hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed: %Rrc"),
Utf8Str(aPath).c_str(),rc);
@@ -1401,7 +2832,7 @@ STDMETHODIMP GuestSession::DirectoryQueryInfo(IN_BSTR aPath, IGuestFsObjInfo **a
{
switch (vrc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
hr = GuestProcess::setErrorExternal(this, guestRc);
break;
@@ -1428,10 +2859,42 @@ STDMETHODIMP GuestSession::DirectoryRemove(IN_BSTR aPath)
#else
LogFlowThisFuncEnter();
+ if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
+ return setError(E_INVALIDARG, tr("No directory to remove specified"));
+
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- ReturnComNotImplemented();
+ HRESULT hr = isReadyExternal();
+ if (FAILED(hr))
+ return hr;
+
+ /* No flags; only remove the directory when empty. */
+ uint32_t uFlags = 0;
+
+ int guestRc;
+ int vrc = directoryRemoveInternal(Utf8Str(aPath), uFlags, &guestRc);
+ if (RT_FAILURE(vrc))
+ {
+ switch (vrc)
+ {
+ case VERR_NOT_SUPPORTED:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Handling removing guest directories not supported by installed Guest Additions"));
+ break;
+
+ case VERR_GSTCTL_GUEST_ERROR:
+ hr = GuestDirectory::setErrorExternal(this, guestRc);
+ break;
+
+ default:
+ hr = setError(VBOX_E_IPRT_ERROR, tr("Removing guest directory \"%s\" failed: %Rrc"),
+ Utf8Str(aPath).c_str(), vrc);
+ break;
+ }
+ }
+
+ return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -1442,10 +2905,63 @@ STDMETHODIMP GuestSession::DirectoryRemoveRecursive(IN_BSTR aPath, ComSafeArrayI
#else
LogFlowThisFuncEnter();
+ if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
+ return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
+
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- ReturnComNotImplemented();
+ HRESULT hr = isReadyExternal();
+ if (FAILED(hr))
+ return hr;
+
+ ComObjPtr<Progress> pProgress;
+ hr = pProgress.createObject();
+ if (SUCCEEDED(hr))
+ hr = pProgress->init(static_cast<IGuestSession *>(this),
+ Bstr(tr("Removing guest directory")).raw(),
+ TRUE /*aCancelable*/);
+ if (FAILED(hr))
+ 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! */
+ hr = pProgress->notifyComplete(S_OK);
+ if (FAILED(hr))
+ return hr;
+
+ /* Remove the directory + all its contents. */
+ uint32_t uFlags = DIRREMOVE_FLAG_RECURSIVE
+ | DIRREMOVE_FLAG_CONTENT_AND_DIR;
+ int guestRc;
+ int vrc = directoryRemoveInternal(Utf8Str(aPath), uFlags, &guestRc);
+ if (RT_FAILURE(vrc))
+ {
+ switch (vrc)
+ {
+ case VERR_NOT_SUPPORTED:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
+ break;
+
+ case VERR_GSTCTL_GUEST_ERROR:
+ hr = GuestFile::setErrorExternal(this, guestRc);
+ break;
+
+ default:
+ hr = setError(VBOX_E_IPRT_ERROR, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
+ Utf8Str(aPath).c_str(), vrc);
+ break;
+ }
+ }
+ else
+ {
+ pProgress.queryInterfaceTo(aProgress);
+ }
+
+ return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -1456,10 +2972,46 @@ STDMETHODIMP GuestSession::DirectoryRename(IN_BSTR aSource, IN_BSTR aDest, ComSa
#else
LogFlowThisFuncEnter();
+ if (RT_UNLIKELY((aSource) == NULL || *(aSource) == '\0'))
+ return setError(E_INVALIDARG, tr("No source directory to rename specified"));
+
+ if (RT_UNLIKELY((aDest) == NULL || *(aDest) == '\0'))
+ return setError(E_INVALIDARG, tr("No destination directory to rename the source to specified"));
+
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- ReturnComNotImplemented();
+ HRESULT hr = isReadyExternal();
+ if (FAILED(hr))
+ return hr;
+
+ /* No flags; only remove the directory when empty. */
+ uint32_t uFlags = 0;
+
+ int guestRc;
+ int vrc = pathRenameInternal(Utf8Str(aSource), Utf8Str(aDest), uFlags, &guestRc);
+ if (RT_FAILURE(vrc))
+ {
+ switch (vrc)
+ {
+ case VERR_NOT_SUPPORTED:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Handling renaming guest directories not supported by installed Guest Additions"));
+ break;
+
+ case VERR_GSTCTL_GUEST_ERROR:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Renaming guest directory failed: %Rrc"), guestRc);
+ break;
+
+ default:
+ hr = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest directory \"%s\" failed: %Rrc"),
+ Utf8Str(aSource).c_str(), vrc);
+ break;
+ }
+ }
+
+ return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -1491,7 +3043,7 @@ STDMETHODIMP GuestSession::EnvironmentClear(void)
mData.mEnvironment.Clear();
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowThisFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -1516,7 +3068,7 @@ STDMETHODIMP GuestSession::EnvironmentGet(IN_BSTR aName, BSTR *aValue)
Bstr strValue(mData.mEnvironment.Get(Utf8Str(aName)));
strValue.cloneTo(aValue);
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowThisFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -1558,7 +3110,7 @@ STDMETHODIMP GuestSession::EnvironmentUnset(IN_BSTR aName)
mData.mEnvironment.Unset(Utf8Str(aName));
- LogFlowFuncLeaveRC(S_OK);
+ LogFlowThisFuncLeave();
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -1603,7 +3155,7 @@ STDMETHODIMP GuestSession::FileExists(IN_BSTR aPath, BOOL *aExists)
switch (vrc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
hr = GuestProcess::setErrorExternal(this, guestRc);
break;
@@ -1642,7 +3194,7 @@ STDMETHODIMP GuestSession::FileRemove(IN_BSTR aPath)
{
switch (vrc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
hr = GuestProcess::setErrorExternal(this, guestRc);
break;
@@ -1657,7 +3209,22 @@ STDMETHODIMP GuestSession::FileRemove(IN_BSTR aPath)
#endif /* VBOX_WITH_GUEST_CONTROL */
}
-STDMETHODIMP GuestSession::FileOpen(IN_BSTR aPath, IN_BSTR aOpenMode, IN_BSTR aDisposition, ULONG aCreationMode, LONG64 aOffset, IGuestFile **aFile)
+STDMETHODIMP GuestSession::FileOpen(IN_BSTR aPath, IN_BSTR aOpenMode, IN_BSTR aDisposition, ULONG aCreationMode, IGuestFile **aFile)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+ ReturnComNotImplemented();
+#else
+ LogFlowThisFuncEnter();
+
+ Bstr strSharingMode = ""; /* Sharing mode is ignored. */
+
+ return FileOpenEx(aPath, aOpenMode, aDisposition, strSharingMode.raw(), aCreationMode,
+ 0 /* aOffset */, aFile);
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
+STDMETHODIMP GuestSession::FileOpenEx(IN_BSTR aPath, IN_BSTR aOpenMode, IN_BSTR aDisposition, IN_BSTR aSharingMode,
+ ULONG aCreationMode, LONG64 aOffset, IGuestFile **aFile)
{
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
@@ -1670,23 +3237,38 @@ STDMETHODIMP GuestSession::FileOpen(IN_BSTR aPath, IN_BSTR aOpenMode, IN_BSTR aD
return setError(E_INVALIDARG, tr("No open mode specified"));
if (RT_UNLIKELY((aDisposition) == NULL || *(aDisposition) == '\0'))
return setError(E_INVALIDARG, tr("No disposition mode specified"));
+ /* aSharingMode is optional. */
CheckComArgOutPointerValid(aFile);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- /** @todo Validate open mode. */
- /** @todo Validate disposition mode. */
+ HRESULT hr = isReadyExternal();
+ if (FAILED(hr))
+ return hr;
/** @todo Validate creation mode. */
uint32_t uCreationMode = 0;
- HRESULT hr = S_OK;
+ GuestFileOpenInfo openInfo;
+ openInfo.mFileName = Utf8Str(aPath);
+ openInfo.mOpenMode = Utf8Str(aOpenMode);
+ openInfo.mDisposition = Utf8Str(aDisposition);
+ openInfo.mSharingMode = Utf8Str(aSharingMode);
+ openInfo.mCreationMode = aCreationMode;
+ openInfo.mInitialOffset = aOffset;
+
+ uint64_t uFlagsIgnored;
+ int vrc = RTFileModeToFlagsEx(openInfo.mOpenMode.c_str(),
+ openInfo.mDisposition.c_str(),
+ openInfo.mSharingMode.c_str(),
+ &uFlagsIgnored);
+ if (RT_FAILURE(vrc))
+ return setError(E_INVALIDARG, tr("Invalid open mode / disposition / sharing mode specified"));
ComObjPtr <GuestFile> pFile; int guestRc;
- int vrc = fileOpenInternal(Utf8Str(aPath), Utf8Str(aOpenMode), Utf8Str(aDisposition),
- aCreationMode, aOffset, pFile, &guestRc);
+ vrc = fileOpenInternal(openInfo, pFile, &guestRc);
if (RT_SUCCESS(vrc))
{
/* Return directory object to the caller. */
@@ -1696,12 +3278,17 @@ STDMETHODIMP GuestSession::FileOpen(IN_BSTR aPath, IN_BSTR aOpenMode, IN_BSTR aD
{
switch (vrc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
- hr = GuestProcess::setErrorExternal(this, guestRc);
+ case VERR_NOT_SUPPORTED:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Handling guest files not supported by installed Guest Additions"));
+ break;
+
+ case VERR_GSTCTL_GUEST_ERROR:
+ hr = GuestFile::setErrorExternal(this, guestRc);
break;
default:
- hr = setError(VBOX_E_IPRT_ERROR, tr("Opening file \"%s\" failed: %Rrc"),
+ hr = setError(VBOX_E_IPRT_ERROR, tr("Opening guest file \"%s\" failed: %Rrc"),
Utf8Str(aPath).c_str(), vrc);
break;
}
@@ -1747,7 +3334,7 @@ STDMETHODIMP GuestSession::FileQueryInfo(IN_BSTR aPath, IGuestFsObjInfo **aInfo)
{
switch (vrc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
hr = GuestProcess::setErrorExternal(this, guestRc);
break;
@@ -1791,7 +3378,7 @@ STDMETHODIMP GuestSession::FileQuerySize(IN_BSTR aPath, LONG64 *aSize)
{
switch (vrc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
hr = GuestProcess::setErrorExternal(this, guestRc);
break;
@@ -1812,10 +3399,47 @@ STDMETHODIMP GuestSession::FileRename(IN_BSTR aSource, IN_BSTR aDest, ComSafeArr
#else
LogFlowThisFuncEnter();
+ if (RT_UNLIKELY((aSource) == NULL || *(aSource) == '\0'))
+ return setError(E_INVALIDARG, tr("No source file to rename specified"));
+
+ if (RT_UNLIKELY((aDest) == NULL || *(aDest) == '\0'))
+ return setError(E_INVALIDARG, tr("No destination file to rename the source to specified"));
+
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- ReturnComNotImplemented();
+ HRESULT hr = isReadyExternal();
+ if (FAILED(hr))
+ return hr;
+
+ /* No flags; only remove the directory when empty. */
+ uint32_t uFlags = 0;
+
+ int guestRc;
+ int vrc = pathRenameInternal(Utf8Str(aSource), Utf8Str(aDest), uFlags, &guestRc);
+ if (RT_FAILURE(vrc))
+ {
+ switch (vrc)
+ {
+ case VERR_NOT_SUPPORTED:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Handling renaming guest files not supported by installed Guest Additions"));
+ break;
+
+ case VERR_GSTCTL_GUEST_ERROR:
+ /** @todo Proper guestRc to text translation needed. */
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Renaming guest file failed: %Rrc"), guestRc);
+ break;
+
+ default:
+ hr = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest file \"%s\" failed: %Rrc"),
+ Utf8Str(aSource).c_str(), vrc);
+ break;
+ }
+ }
+
+ return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -1841,10 +3465,10 @@ STDMETHODIMP GuestSession::ProcessCreate(IN_BSTR aCommand, ComSafeArrayIn(IN_BST
#else
LogFlowThisFuncEnter();
- com::SafeArray<LONG> affinity;
+ com::SafeArray<LONG> affinityIgnored;
return ProcessCreateEx(aCommand, ComSafeArrayInArg(aArguments), ComSafeArrayInArg(aEnvironment),
- ComSafeArrayInArg(aFlags), aTimeoutMS, ProcessPriority_Default, ComSafeArrayAsInParam(affinity), aProcess);
+ ComSafeArrayInArg(aFlags), aTimeoutMS, ProcessPriority_Default, ComSafeArrayAsInParam(affinityIgnored), aProcess);
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -1865,6 +3489,10 @@ STDMETHODIMP GuestSession::ProcessCreateEx(IN_BSTR aCommand, ComSafeArrayIn(IN_B
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
+ HRESULT hr = isReadyExternal();
+ if (FAILED(hr))
+ return hr;
+
GuestProcessStartupInfo procInfo;
procInfo.mCommand = Utf8Str(aCommand);
@@ -1892,8 +3520,6 @@ STDMETHODIMP GuestSession::ProcessCreateEx(IN_BSTR aCommand, ComSafeArrayIn(IN_B
rc = procInfo.mEnvironment.Set(Utf8Str(environment[i]));
}
- HRESULT hr = S_OK;
-
if (RT_SUCCESS(rc))
{
if (aFlags)
@@ -1909,7 +3535,10 @@ STDMETHODIMP GuestSession::ProcessCreateEx(IN_BSTR aCommand, ComSafeArrayIn(IN_B
{
com::SafeArray<LONG> affinity(ComSafeArrayInArg(aAffinity));
for (size_t i = 0; i < affinity.size(); i++)
- procInfo.mAffinity[i] = affinity[i]; /** @todo Really necessary? Later. */
+ {
+ if (affinity[i])
+ procInfo.mAffinity |= (uint64_t)1 << i;
+ }
}
procInfo.mPriority = aPriority;
@@ -1933,7 +3562,7 @@ STDMETHODIMP GuestSession::ProcessCreateEx(IN_BSTR aCommand, ComSafeArrayIn(IN_B
switch (rc)
{
case VERR_MAX_PROCS_REACHED:
- hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest objects per session (%ld) reached"),
+ hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest processes per session (%ld) reached"),
VBOX_GUESTCTRL_MAX_OBJECTS);
break;
@@ -1955,7 +3584,7 @@ STDMETHODIMP GuestSession::ProcessGet(ULONG aPID, IGuestProcess **aProcess)
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
#else
- LogFlowThisFunc(("aPID=%RU32\n", aPID));
+ LogFlowThisFunc(("PID=%RU32\n", aPID));
CheckComArgOutPointerValid(aProcess);
if (aPID == 0)
@@ -2053,3 +3682,78 @@ STDMETHODIMP GuestSession::SymlinkRemoveFile(IN_BSTR aFile)
#endif /* VBOX_WITH_GUEST_CONTROL */
}
+STDMETHODIMP GuestSession::WaitFor(ULONG aWaitFlags, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+ ReturnComNotImplemented();
+#else
+ LogFlowThisFuncEnter();
+
+ CheckComArgOutPointerValid(aReason);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ /*
+ * Note: Do not hold any locks here while waiting!
+ */
+ HRESULT hr = S_OK;
+
+ int guestRc; GuestSessionWaitResult_T waitResult;
+ int vrc = waitFor(aWaitFlags, aTimeoutMS, waitResult, &guestRc);
+ if (RT_SUCCESS(vrc))
+ {
+ *aReason = waitResult;
+ }
+ else
+ {
+ switch (vrc)
+ {
+ case VERR_GSTCTL_GUEST_ERROR:
+ hr = GuestSession::setErrorExternal(this, guestRc);
+ break;
+
+ case VERR_TIMEOUT:
+ *aReason = GuestSessionWaitResult_Timeout;
+ break;
+
+ default:
+ {
+ const char *pszSessionName = mData.mSession.mName.c_str();
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Waiting for guest session \"%s\" failed: %Rrc"),
+ pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
+ break;
+ }
+ }
+ }
+
+ LogFlowFuncLeaveRC(vrc);
+ return hr;
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
+STDMETHODIMP GuestSession::WaitForArray(ComSafeArrayIn(GuestSessionWaitForFlag_T, aFlags), ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+ ReturnComNotImplemented();
+#else
+ LogFlowThisFuncEnter();
+
+ CheckComArgOutPointerValid(aReason);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ /*
+ * Note: Do not hold any locks here while waiting!
+ */
+ uint32_t fWaitFor = GuestSessionWaitForFlag_None;
+ com::SafeArray<GuestSessionWaitForFlag_T> flags(ComSafeArrayInArg(aFlags));
+ for (size_t i = 0; i < flags.size(); i++)
+ fWaitFor |= flags[i];
+
+ return WaitFor(fWaitFor, aTimeoutMS, aReason);
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+