summaryrefslogtreecommitdiff
path: root/src/VBox/Main/src-client/GuestCtrlImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-client/GuestCtrlImpl.cpp')
-rw-r--r--src/VBox/Main/src-client/GuestCtrlImpl.cpp324
1 files changed, 239 insertions, 85 deletions
diff --git a/src/VBox/Main/src-client/GuestCtrlImpl.cpp b/src/VBox/Main/src-client/GuestCtrlImpl.cpp
index 42c97541..384d5f57 100644
--- a/src/VBox/Main/src-client/GuestCtrlImpl.cpp
+++ b/src/VBox/Main/src-client/GuestCtrlImpl.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2012 Oracle Corporation
+ * Copyright (C) 2006-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;
@@ -22,6 +22,7 @@
#include "Global.h"
#include "ConsoleImpl.h"
#include "ProgressImpl.h"
+#include "VBoxEvents.h"
#include "VMMDev.h"
#include "AutoCaller.h"
@@ -64,8 +65,8 @@
/* static */
DECLCALLBACK(int) Guest::notifyCtrlDispatcher(void *pvExtension,
uint32_t u32Function,
- void *pvParms,
- uint32_t cbParms)
+ void *pvData,
+ uint32_t cbData)
{
using namespace guestControl;
@@ -74,7 +75,7 @@ DECLCALLBACK(int) Guest::notifyCtrlDispatcher(void *pvExtension,
* changes to the object state.
*/
LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
- pvExtension, u32Function, pvParms, cbParms));
+ pvExtension, u32Function, pvData, cbData));
ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);
Assert(!pGuest.isNull());
@@ -85,58 +86,35 @@ DECLCALLBACK(int) Guest::notifyCtrlDispatcher(void *pvExtension,
* - Extract the session ID out of the context ID
* - Dispatch the whole stuff to the appropriate session (if still exists)
*/
+ if (cbData != sizeof(VBOXGUESTCTRLHOSTCALLBACK))
+ return VERR_NOT_SUPPORTED;
+ PVBOXGUESTCTRLHOSTCALLBACK pSvcCb = (PVBOXGUESTCTRLHOSTCALLBACK)pvData;
+ AssertPtr(pSvcCb);
- PCALLBACKHEADER pHeader = (PCALLBACKHEADER)pvParms;
- AssertPtr(pHeader);
+ if (!pSvcCb->mParms) /* At least context ID must be present. */
+ return VERR_INVALID_PARAMETER;
+ uint32_t uContextID;
+ int rc = pSvcCb->mpaParms[0].getUInt32(&uContextID);
+ AssertMsgRCReturn(rc, ("Unable to extract callback context ID, pvData=%p\n", pSvcCb), rc);
#ifdef DEBUG
LogFlowFunc(("CID=%RU32, uSession=%RU32, uObject=%RU32, uCount=%RU32\n",
- pHeader->u32ContextID,
- VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHeader->u32ContextID),
- VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pHeader->u32ContextID),
- VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(pHeader->u32ContextID)));
+ uContextID,
+ VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID),
+ VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(uContextID),
+ VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID)));
#endif
- bool fDispatch = true;
-#ifdef DEBUG
- /*
- * Pre-check: If we got a status message with an error and VERR_TOO_MUCH_DATA
- * it means that that guest could not handle the entire message
- * because of its exceeding size. This should not happen on daily
- * use but testcases might try this. It then makes no sense to dispatch
- * this further because we don't have a valid context ID.
- */
- if (u32Function == GUEST_EXEC_SEND_STATUS)
- {
- PCALLBACKDATAEXECSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvParms);
- AssertPtr(pCallbackData);
- AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbParms, VERR_INVALID_PARAMETER);
- AssertReturn(CALLBACKDATAMAGIC_EXEC_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
-
- if ( pCallbackData->u32Status == PROC_STS_ERROR
- && ((int)pCallbackData->u32Flags) == VERR_TOO_MUCH_DATA)
- {
- LogFlowFunc(("Requested command with too much data, skipping dispatching ...\n"));
+ VBOXGUESTCTRLHOSTCBCTX ctxCb = { u32Function, uContextID };
+ rc = pGuest->dispatchToSession(&ctxCb, pSvcCb);
- Assert(pCallbackData->u32PID == 0);
- fDispatch = false;
- }
- }
-#endif
- int rc = VINF_SUCCESS;
- if (fDispatch)
- {
- rc = pGuest->dispatchToSession(pHeader->u32ContextID, u32Function, pvParms, cbParms);
- if (RT_SUCCESS(rc))
- return rc;
- }
-
- LogFlowFuncLeaveRC(rc);
+ LogFlowFunc(("Returning rc=%Rrc\n", rc));
return rc;
}
#endif /* VBOX_WITH_GUEST_CONTROL */
-STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(AdditionsUpdateFlag_T, aFlags), IProgress **aProgress)
+STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(IN_BSTR, aArguments),
+ ComSafeArrayIn(AdditionsUpdateFlag_T, aFlags), IProgress **aProgress)
{
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
@@ -151,7 +129,7 @@ STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(Additio
uint32_t fFlags = AdditionsUpdateFlag_None;
if (aFlags)
{
- com::SafeArray<CopyFileFlag_T> flags(ComSafeArrayInArg(aFlags));
+ com::SafeArray<AdditionsUpdateFlag_T> flags(ComSafeArrayInArg(aFlags));
for (size_t i = 0; i < flags.size(); i++)
fFlags |= flags[i];
}
@@ -162,19 +140,44 @@ STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(Additio
return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
}
+ int rc = VINF_SUCCESS;
+
+ ProcessArguments aArgs;
+ if (aArguments)
+ {
+ try
+ {
+ com::SafeArray<IN_BSTR> arguments(ComSafeArrayInArg(aArguments));
+ for (size_t i = 0; i < arguments.size(); i++)
+ aArgs.push_back(Utf8Str(arguments[i]));
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
HRESULT hr = S_OK;
- /* Create an anonymous session. This is required to run the Guest Additions
- * update process with administrative rights. */
+ /*
+ * Create an anonymous session. This is required to run the Guest Additions
+ * update process with administrative rights.
+ */
+ GuestSessionStartupInfo startupInfo;
+ startupInfo.mName = "Updating Guest Additions";
+
+ GuestCredentials guestCreds;
+ RT_ZERO(guestCreds);
+
ComObjPtr<GuestSession> pSession;
- int rc = sessionCreate("" /* User */, "" /* Password */, "" /* Domain */,
- "Updating Guest Additions" /* Name */, pSession);
+ if (RT_SUCCESS(rc))
+ rc = sessionCreate(startupInfo, guestCreds, pSession);
if (RT_FAILURE(rc))
{
switch (rc)
{
case VERR_MAX_PROCS_REACHED:
- hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
+ hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest sessions (%ld) reached"),
VBOX_GUESTCTRL_MAX_SESSIONS);
break;
@@ -188,10 +191,13 @@ STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(Additio
else
{
Assert(!pSession.isNull());
- rc = pSession->queryInfo();
+ int guestRc;
+ rc = pSession->startSessionInternal(&guestRc);
if (RT_FAILURE(rc))
{
- hr = setError(VBOX_E_IPRT_ERROR, tr("Could not query guest session information: %Rrc"), rc);
+ /** @todo Handle guestRc! */
+
+ hr = setError(VBOX_E_IPRT_ERROR, tr("Could not open guest session: %Rrc"), rc);
}
else
{
@@ -199,7 +205,7 @@ STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(Additio
{
ComObjPtr<Progress> pProgress;
SessionTaskUpdateAdditions *pTask = new SessionTaskUpdateAdditions(pSession /* GuestSession */,
- Utf8Str(aSource), fFlags);
+ Utf8Str(aSource), aArgs, fFlags);
rc = pSession->startTaskAsync(tr("Updating Guest Additions"), pTask, pProgress);
if (RT_SUCCESS(rc))
{
@@ -223,27 +229,113 @@ STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(Additio
// private methods
/////////////////////////////////////////////////////////////////////////////
-int Guest::dispatchToSession(uint32_t uContextID, uint32_t uFunction, void *pvData, size_t cbData)
+int Guest::dispatchToSession(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
{
- LogFlowFuncEnter();
+ LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
+
+ AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("uFunction=%RU32, uContextID=%RU32, uProtocol=%RU32\n",
+ pCtxCb->uFunction, pCtxCb->uContextID, pCtxCb->uProtocol));
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- uint32_t uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID);
+ uint32_t uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtxCb->uContextID);
#ifdef DEBUG
- LogFlowFunc(("uSessionID=%RU32 (%RU32 total)\n",
+ LogFlowFunc(("uSessionID=%RU32 (%zu total)\n",
uSessionID, mData.mGuestSessions.size()));
#endif
- int rc;
GuestSessions::const_iterator itSession
= mData.mGuestSessions.find(uSessionID);
+
+ int rc;
if (itSession != mData.mGuestSessions.end())
{
ComObjPtr<GuestSession> pSession(itSession->second);
Assert(!pSession.isNull());
alock.release();
- rc = pSession->dispatchToProcess(uContextID, uFunction, pvData, cbData);
+
+ bool fDispatch = true;
+#ifdef DEBUG
+ /*
+ * Pre-check: If we got a status message with an error and VERR_TOO_MUCH_DATA
+ * it means that that guest could not handle the entire message
+ * because of its exceeding size. This should not happen on daily
+ * use but testcases might try this. It then makes no sense to dispatch
+ * this further because we don't have a valid context ID.
+ */
+ if ( pCtxCb->uFunction == GUEST_EXEC_STATUS
+ && pSvcCb->mParms >= 5)
+ {
+ CALLBACKDATA_PROC_STATUS dataCb;
+ /* pSvcCb->mpaParms[0] always contains the context ID. */
+ pSvcCb->mpaParms[1].getUInt32(&dataCb.uPID);
+ pSvcCb->mpaParms[2].getUInt32(&dataCb.uStatus);
+ pSvcCb->mpaParms[3].getUInt32(&dataCb.uFlags);
+ pSvcCb->mpaParms[4].getPointer(&dataCb.pvData, &dataCb.cbData);
+
+ if ( ( dataCb.uStatus == PROC_STS_ERROR)
+ /** @todo Note: Due to legacy reasons we cannot change uFlags to
+ * int32_t, so just cast it for now. */
+ && ((int32_t)dataCb.uFlags == VERR_TOO_MUCH_DATA))
+ {
+ LogFlowFunc(("Requested command with too much data, skipping dispatching ...\n"));
+
+ Assert(dataCb.uPID == 0);
+ fDispatch = false;
+ }
+ }
+#endif
+ if (fDispatch)
+ {
+ switch (pCtxCb->uFunction)
+ {
+ case GUEST_DISCONNECTED:
+ rc = pSession->dispatchToThis(pCtxCb, pSvcCb);
+ break;
+
+ case GUEST_EXEC_STATUS:
+ case GUEST_EXEC_OUTPUT:
+ case GUEST_EXEC_INPUT_STATUS:
+ case GUEST_EXEC_IO_NOTIFY:
+ rc = pSession->dispatchToProcess(pCtxCb, pSvcCb);
+ break;
+
+ case GUEST_FILE_NOTIFY:
+ rc = pSession->dispatchToFile(pCtxCb, pSvcCb);
+ break;
+
+ case GUEST_SESSION_NOTIFY:
+ rc = pSession->dispatchToThis(pCtxCb, pSvcCb);
+ break;
+
+ default:
+ /*
+ * Try processing generic messages which might
+ * (or might not) supported by certain objects.
+ * If the message either is not found or supported
+ * by the approprirate object, try handling it
+ * in this session object.
+ */
+ rc = pSession->dispatchToObject(pCtxCb, pSvcCb);
+ if ( rc == VERR_NOT_FOUND
+ || rc == VERR_NOT_SUPPORTED)
+ {
+ alock.acquire();
+
+ rc = pSession->dispatchGeneric(pCtxCb, pSvcCb);
+ }
+#ifndef DEBUG_andy
+ if (rc == VERR_NOT_IMPLEMENTED)
+ AssertMsgFailed(("Received not handled function %RU32\n", pCtxCb->uFunction));
+#endif
+ break;
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
}
else
rc = VERR_NOT_FOUND;
@@ -254,35 +346,54 @@ int Guest::dispatchToSession(uint32_t uContextID, uint32_t uFunction, void *pvDa
int Guest::sessionRemove(GuestSession *pSession)
{
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+
LogFlowThisFuncEnter();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
int rc = VERR_NOT_FOUND;
- LogFlowFunc(("Closing session (ID=%RU32) ...\n", pSession->getId()));
+ LogFlowThisFunc(("Removing session (ID=%RU32) ...\n", pSession->getId()));
- for (GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
- itSessions != mData.mGuestSessions.end(); ++itSessions)
+ GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
+ while (itSessions != mData.mGuestSessions.end())
{
if (pSession == itSessions->second)
{
- LogFlowFunc(("Removing session (pSession=%p, ID=%RU32) (now total %ld sessions)\n",
- (GuestSession *)itSessions->second, itSessions->second->getId(), mData.mGuestSessions.size() - 1));
+#ifdef DEBUG_andy
+ ULONG cRefs = pSession->AddRef();
+ Assert(cRefs >= 2);
+ LogFlowThisFunc(("pCurSession=%p, cRefs=%RU32\n", pSession, cRefs - 2));
+ pSession->Release();
+#endif
+ /* Make sure to consume the pointer before the one of the
+ * iterator gets released. */
+ ComObjPtr<GuestSession> pCurSession = pSession;
+
+ LogFlowThisFunc(("Removing session (pSession=%p, ID=%RU32) (now total %ld sessions)\n",
+ pSession, pSession->getId(), mData.mGuestSessions.size() - 1));
+ rc = pSession->onRemove();
mData.mGuestSessions.erase(itSessions);
- rc = VINF_SUCCESS;
+ alock.release(); /* Release lock before firing off event. */
+
+ fireGuestSessionRegisteredEvent(mEventSource, pCurSession,
+ false /* Unregistered */);
+ pCurSession.setNull();
break;
}
+
+ itSessions++;
}
LogFlowFuncLeaveRC(rc);
return rc;
}
-int Guest::sessionCreate(const Utf8Str &strUser, const Utf8Str &strPassword, const Utf8Str &strDomain,
- const Utf8Str &strSessionName, ComObjPtr<GuestSession> &pGuestSession)
+int Guest::sessionCreate(const GuestSessionStartupInfo &ssInfo,
+ const GuestCredentials &guestCreds, ComObjPtr<GuestSession> &pGuestSession)
{
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
@@ -293,7 +404,7 @@ int Guest::sessionCreate(const Utf8Str &strUser, const Utf8Str &strPassword, con
try
{
/* Create a new session ID and assign it. */
- uint32_t uNewSessionID = 0;
+ uint32_t uNewSessionID = VBOX_GUESTCTRL_SESSION_ID_BASE;
uint32_t uTries = 0;
for (;;)
@@ -306,9 +417,9 @@ int Guest::sessionCreate(const Utf8Str &strUser, const Utf8Str &strPassword, con
}
uNewSessionID++;
if (uNewSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS)
- uNewSessionID = 0;
+ uNewSessionID = VBOX_GUESTCTRL_SESSION_ID_BASE;
- if (++uTries == UINT32_MAX)
+ if (++uTries == VBOX_GUESTCTRL_MAX_SESSIONS)
break; /* Don't try too hard. */
}
if (RT_FAILURE(rc)) throw rc;
@@ -317,20 +428,48 @@ int Guest::sessionCreate(const Utf8Str &strUser, const Utf8Str &strPassword, con
HRESULT hr = pGuestSession.createObject();
if (FAILED(hr)) throw VERR_COM_UNEXPECTED;
- rc = pGuestSession->init(this, uNewSessionID,
- strUser, strPassword, strDomain, strSessionName);
+ /** @todo Use an overloaded copy operator. Later. */
+ GuestSessionStartupInfo startupInfo;
+ startupInfo.mID = uNewSessionID; /* Assign new session ID. */
+ startupInfo.mName = ssInfo.mName;
+ startupInfo.mOpenFlags = ssInfo.mOpenFlags;
+ startupInfo.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS;
+
+ GuestCredentials guestCredentials;
+ if (!guestCreds.mUser.isEmpty())
+ {
+ /** @todo Use an overloaded copy operator. Later. */
+ guestCredentials.mUser = guestCreds.mUser;
+ guestCredentials.mPassword = guestCreds.mPassword;
+ guestCredentials.mDomain = guestCreds.mDomain;
+ }
+ else
+ {
+ /* Internal (annonymous) session. */
+ startupInfo.mIsInternal = true;
+ }
+
+ rc = pGuestSession->init(this, startupInfo, guestCredentials);
if (RT_FAILURE(rc)) throw rc;
+ /*
+ * Add session object to our session map. This is necessary
+ * before calling openSession because the guest calls back
+ * with the creation result of this session.
+ */
mData.mGuestSessions[uNewSessionID] = pGuestSession;
- LogFlowFunc(("Added new session (pSession=%p, ID=%RU32), now %ld sessions total\n",
- (GuestSession *)pGuestSession, uNewSessionID, mData.mGuestSessions.size()));
+ alock.release(); /* Release lock before firing off event. */
+
+ fireGuestSessionRegisteredEvent(mEventSource, pGuestSession,
+ true /* Registered */);
}
catch (int rc2)
{
rc = rc2;
}
+ LogFlowFuncLeaveRC(rc);
return rc;
}
@@ -343,7 +482,8 @@ inline bool Guest::sessionExists(uint32_t uSessionID)
// implementation of public methods
/////////////////////////////////////////////////////////////////////////////
-STDMETHODIMP Guest::CreateSession(IN_BSTR aUser, IN_BSTR aPassword, IN_BSTR aDomain, IN_BSTR aSessionName, IGuestSession **aGuestSession)
+STDMETHODIMP Guest::CreateSession(IN_BSTR aUser, IN_BSTR aPassword, IN_BSTR aDomain,
+ IN_BSTR aSessionName, IGuestSession **aGuestSession)
{
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
@@ -351,48 +491,62 @@ STDMETHODIMP Guest::CreateSession(IN_BSTR aUser, IN_BSTR aPassword, IN_BSTR aDom
LogFlowFuncEnter();
- /* Do not allow anonymous sessions (with system rights) with official API. */
+ /* Do not allow anonymous sessions (with system rights) with public API. */
if (RT_UNLIKELY((aUser) == NULL || *(aUser) == '\0'))
return setError(E_INVALIDARG, tr("No user name specified"));
+ if (RT_UNLIKELY((aPassword) == NULL)) /* Allow empty passwords. */
+ return setError(E_INVALIDARG, tr("No password specified"));
CheckComArgOutPointerValid(aGuestSession);
/* Rest is optional. */
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- HRESULT hr = S_OK;
+ GuestSessionStartupInfo startupInfo;
+ startupInfo.mName = aSessionName;
+
+ GuestCredentials guestCreds;
+ guestCreds.mUser = aUser;
+ guestCreds.mPassword = aPassword;
+ guestCreds.mDomain = aDomain;
ComObjPtr<GuestSession> pSession;
- int rc = sessionCreate(aUser, aPassword, aDomain, aSessionName, pSession);
+ int rc = sessionCreate(startupInfo, guestCreds, pSession);
if (RT_SUCCESS(rc))
{
/* Return guest session to the caller. */
HRESULT hr2 = pSession.queryInterfaceTo(aGuestSession);
if (FAILED(hr2))
rc = VERR_COM_OBJECT_NOT_FOUND;
+ }
- if (RT_SUCCESS(rc))
- rc = pSession->queryInfo();
+ if (RT_SUCCESS(rc))
+ {
+ /* Start (fork) the session asynchronously
+ * on the guest. */
+ rc = pSession->startSessionAsync();
}
+ HRESULT hr = S_OK;
+
if (RT_FAILURE(rc))
{
switch (rc)
{
case VERR_MAX_PROCS_REACHED:
- hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
+ hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest sessions (%ld) reached"),
VBOX_GUESTCTRL_MAX_SESSIONS);
break;
/** @todo Add more errors here. */
- default:
- hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session, rc=%Rrc"), rc);
+ default:
+ hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session: %Rrc"), rc);
break;
}
}
- LogFlowFuncLeaveRC(rc);
+ LogFlowThisFunc(("Returning rc=%Rhrc\n", hr));
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}