summaryrefslogtreecommitdiff
path: root/src/VBox/Main/src-client
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-client')
-rw-r--r--src/VBox/Main/src-client/AudioSnifferInterface.cpp6
-rw-r--r--src/VBox/Main/src-client/BusAssignmentManager.cpp7
-rw-r--r--src/VBox/Main/src-client/ClientTokenHolder.cpp334
-rw-r--r--src/VBox/Main/src-client/ConsoleImpl.cpp2678
-rw-r--r--src/VBox/Main/src-client/ConsoleImpl2.cpp1403
-rw-r--r--src/VBox/Main/src-client/ConsoleImplTeleporter.cpp31
-rw-r--r--src/VBox/Main/src-client/ConsoleVRDPServer.cpp872
-rw-r--r--src/VBox/Main/src-client/DisplayImpl.cpp1649
-rw-r--r--src/VBox/Main/src-client/EbmlWriter.cpp504
-rw-r--r--src/VBox/Main/src-client/EbmlWriter.h291
-rw-r--r--src/VBox/Main/src-client/EmulatedUSBImpl.cpp692
-rw-r--r--src/VBox/Main/src-client/GuestCtrlImpl.cpp324
-rw-r--r--src/VBox/Main/src-client/GuestCtrlPrivate.cpp979
-rw-r--r--src/VBox/Main/src-client/GuestDirectoryImpl.cpp236
-rw-r--r--src/VBox/Main/src-client/GuestDnDImpl.cpp2
-rw-r--r--src/VBox/Main/src-client/GuestFileImpl.cpp1287
-rw-r--r--src/VBox/Main/src-client/GuestFsObjInfoImpl.cpp3
-rw-r--r--src/VBox/Main/src-client/GuestImpl.cpp324
-rw-r--r--src/VBox/Main/src-client/GuestProcessImpl.cpp2149
-rw-r--r--src/VBox/Main/src-client/GuestSessionImpl.cpp2272
-rw-r--r--src/VBox/Main/src-client/GuestSessionImplTasks.cpp245
-rw-r--r--src/VBox/Main/src-client/HGCM.cpp37
-rw-r--r--src/VBox/Main/src-client/HGCMThread.cpp6
-rw-r--r--src/VBox/Main/src-client/KeyboardImpl.cpp81
-rw-r--r--src/VBox/Main/src-client/MachineDebuggerImpl.cpp366
-rw-r--r--src/VBox/Main/src-client/MouseImpl.cpp467
-rw-r--r--src/VBox/Main/src-client/Nvram.cpp378
-rw-r--r--src/VBox/Main/src-client/PCIRawDevImpl.cpp38
-rw-r--r--src/VBox/Main/src-client/RemoteUSBBackend.cpp10
-rw-r--r--src/VBox/Main/src-client/RemoteUSBDeviceImpl.cpp2
-rw-r--r--src/VBox/Main/src-client/SessionImpl.cpp599
-rw-r--r--src/VBox/Main/src-client/USBDeviceImpl.cpp6
-rw-r--r--src/VBox/Main/src-client/UsbCardReader.cpp381
-rw-r--r--src/VBox/Main/src-client/UsbWebcamInterface.cpp472
-rw-r--r--src/VBox/Main/src-client/VBoxDriversRegister.cpp8
-rw-r--r--src/VBox/Main/src-client/VMMDevInterface.cpp120
-rw-r--r--src/VBox/Main/src-client/VideoRec.cpp885
-rw-r--r--src/VBox/Main/src-client/VideoRec.h39
-rw-r--r--src/VBox/Main/src-client/VirtualBoxClientImpl.cpp36
-rw-r--r--src/VBox/Main/src-client/win/VBoxC.def2
-rw-r--r--src/VBox/Main/src-client/win/VBoxClient-x86.def26
-rw-r--r--src/VBox/Main/src-client/win/VBoxClient-x86.rc67
-rw-r--r--src/VBox/Main/src-client/win/dllmain.cpp327
-rw-r--r--src/VBox/Main/src-client/xpcom/module.cpp27
44 files changed, 15307 insertions, 5361 deletions
diff --git a/src/VBox/Main/src-client/AudioSnifferInterface.cpp b/src/VBox/Main/src-client/AudioSnifferInterface.cpp
index f1b64208..c5b902e4 100644
--- a/src/VBox/Main/src-client/AudioSnifferInterface.cpp
+++ b/src/VBox/Main/src-client/AudioSnifferInterface.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -160,9 +160,9 @@ DECLCALLBACK(void *) AudioSniffer::drvQueryInterface(PPDMIBASE pInterface, const
*/
DECLCALLBACK(void) AudioSniffer::drvDestruct(PPDMDRVINS pDrvIns)
{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
PDRVAUDIOSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOSNIFFER);
LogFlow(("AudioSniffer::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
- PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
if (pThis->pAudioSniffer)
{
@@ -178,10 +178,10 @@ DECLCALLBACK(void) AudioSniffer::drvDestruct(PPDMDRVINS pDrvIns)
*/
DECLCALLBACK(int) AudioSniffer::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
PDRVAUDIOSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOSNIFFER);
LogFlow(("AudioSniffer::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
- PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
/*
* Validate configuration.
diff --git a/src/VBox/Main/src-client/BusAssignmentManager.cpp b/src/VBox/Main/src-client/BusAssignmentManager.cpp
index 18e34741..068bb79e 100644
--- a/src/VBox/Main/src-client/BusAssignmentManager.cpp
+++ b/src/VBox/Main/src-client/BusAssignmentManager.cpp
@@ -6,7 +6,7 @@
*/
/*
- * Copyright (C) 2010-2012 Oracle Corporation
+ * Copyright (C) 2010-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;
@@ -77,7 +77,8 @@ static const DeviceAssignmentRule aGenericRules[] =
/* Network controllers */
/* the first network card gets the PCI ID 3, the next 3 gets 8..10,
- * next 4 get 16..19. */
+ * next 4 get 16..19. In "VMWare compatibility" mode the IDs 3 and 17
+ * swap places, i.e. the first card goes to ID 17=0x11. */
{"nic", 0, 3, 0, 1},
{"nic", 0, 8, 0, 1},
{"nic", 0, 9, 0, 1},
@@ -86,8 +87,6 @@ static const DeviceAssignmentRule aGenericRules[] =
{"nic", 0, 17, 0, 1},
{"nic", 0, 18, 0, 1},
{"nic", 0, 19, 0, 1},
- /* VMWare assigns first NIC to slot 11 */
- {"nic-vmware", 0, 11, 0, 1},
/* ISA/LPC controller */
{"lpc", 0, 31, 0, 0},
diff --git a/src/VBox/Main/src-client/ClientTokenHolder.cpp b/src/VBox/Main/src-client/ClientTokenHolder.cpp
new file mode 100644
index 00000000..07ad83e2
--- /dev/null
+++ b/src/VBox/Main/src-client/ClientTokenHolder.cpp
@@ -0,0 +1,334 @@
+/** @file
+ *
+ * VirtualBox API client session token holder (in the client process)
+ */
+
+/*
+ * 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;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/semaphore.h>
+#include <iprt/process.h>
+
+#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
+# include <errno.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sys/ipc.h>
+# include <sys/sem.h>
+#endif
+
+#include <VBox/com/defs.h>
+
+#include "ClientTokenHolder.h"
+#include "SessionImpl.h"
+
+
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+/** client token holder thread */
+static DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD Thread, void *pvUser);
+#endif
+
+
+Session::ClientTokenHolder::ClientTokenHolder()
+{
+ AssertReleaseFailed();
+}
+
+Session::ClientTokenHolder::~ClientTokenHolder()
+{
+ /* release the client token */
+#if defined(RT_OS_WINDOWS)
+
+ if (mSem && mThreadSem)
+ {
+ /*
+ * tell the thread holding the token to release it;
+ * it will close mSem handle
+ */
+ ::SetEvent(mSem);
+ /* wait for the thread to finish */
+ ::WaitForSingleObject(mThreadSem, INFINITE);
+ ::CloseHandle(mThreadSem);
+
+ mThreadSem = NULL;
+ mSem = NULL;
+ mThread = NIL_RTTHREAD;
+ }
+
+#elif defined(RT_OS_OS2)
+
+ if (mThread != NIL_RTTHREAD)
+ {
+ Assert(mSem != NIL_RTSEMEVENT);
+
+ /* tell the thread holding the token to release it */
+ int vrc = RTSemEventSignal(mSem);
+ AssertRC(vrc == NO_ERROR);
+
+ /* wait for the thread to finish */
+ vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT);
+ Assert(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
+
+ mThread = NIL_RTTHREAD;
+ }
+
+ if (mSem != NIL_RTSEMEVENT)
+ {
+ RTSemEventDestroy(mSem);
+ mSem = NIL_RTSEMEVENT;
+ }
+
+#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
+
+ if (mSem >= 0)
+ {
+ ::sembuf sop = { 0, 1, SEM_UNDO };
+ ::semop(mSem, &sop, 1);
+
+ mSem = -1;
+ }
+
+#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
+
+ if (!mToken.isNull())
+ {
+ mToken->Abandon();
+ mToken.setNull();
+ }
+
+#else
+# error "Port me!"
+#endif
+}
+
+#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
+Session::ClientTokenHolder::ClientTokenHolder(const Utf8Str &strTokenId) :
+ mClientTokenId(strTokenId)
+#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
+Session::ClientTokenHolder::ClientTokenHolder(IToken *aToken) :
+ mToken(aToken)
+#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
+{
+#ifdef CTHSEMTYPE
+ mSem = CTHSEMARG;
+#endif
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ mThread = NIL_RTTHREAD;
+#endif
+
+#if defined(RT_OS_WINDOWS)
+ mThreadSem = CTHTHREADSEMARG;
+
+ Bstr bstrTokenId(strTokenId);
+
+ /*
+ * Since there is no guarantee that the constructor and destructor will be
+ * called in the same thread, we need a separate thread to hold the token.
+ */
+
+ mThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
+ AssertMsgReturnVoid(mThreadSem,
+ ("Cannot create an event sem, err=%d", ::GetLastError()));
+
+ void *data[3];
+ data[0] = (void*)(BSTR)bstrTokenId.raw();
+ data[1] = (void*)mThreadSem;
+ data[2] = 0; /* will get an output from the thread */
+
+ /* create a thread to hold the token until signalled to release it */
+ int vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
+ AssertRCReturnVoid(vrc);
+
+ /* wait until thread init is completed */
+ DWORD wrc = ::WaitForSingleObject(mThreadSem, INFINITE);
+ AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
+ Assert(data[2]);
+
+ if (wrc == WAIT_OBJECT_0 && data[2])
+ {
+ /* memorize the event sem we should signal in close() */
+ mSem = (HANDLE)data[2];
+ }
+ else
+ {
+ ::CloseHandle(mThreadSem);
+ mThreadSem = NULL;
+ }
+#elif defined(RT_OS_OS2)
+ Bstr bstrTokenId(strTokenId);
+
+ /*
+ * Since there is no guarantee that the constructor and destructor will be
+ * called in the same thread, we need a separate thread to hold the token.
+ */
+
+ int vrc = RTSemEventCreate(&mSem);
+ AssertRCReturnVoid(vrc);
+
+ void *data[3];
+ data[0] = (void*)bstrTokenId.raw();
+ data[1] = (void*)mSem;
+ data[2] = (void*)false; /* will get the thread result here */
+
+ /* create a thread to hold the token until signalled to release it */
+ vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void *) data,
+ 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
+ AssertRCReturnVoid(vrc);
+ /* wait until thread init is completed */
+ vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT);
+ AssertReturnVoid(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
+
+ /* the thread must succeed */
+ AssertReturnVoid((bool)data[2]);
+
+#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
+
+# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
+ key_t key = RTStrToUInt32(strTokenId.c_str());
+ AssertMsgReturnVoid(key != 0,
+ ("Key value of 0 is not valid for client token"));
+# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
+ char *pszSemName = NULL;
+ RTStrUtf8ToCurrentCP(&pszSemName, strTokenId);
+ key_t key = ::ftok(pszSemName, 'V');
+ RTStrFree(pszSemName);
+# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
+ int s = ::semget(key, 0, 0);
+ AssertMsgReturnVoid(s >= 0,
+ ("Cannot open semaphore, errno=%d", errno));
+
+ /* grab the semaphore */
+ ::sembuf sop = { 0, -1, SEM_UNDO };
+ int rv = ::semop(s, &sop, 1);
+ AssertMsgReturnVoid(rv == 0,
+ ("Cannot grab semaphore, errno=%d", errno));
+ mSem = s;
+
+#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
+
+ /* nothing to do */
+
+#else
+# error "Port me!"
+#endif
+}
+
+bool Session::ClientTokenHolder::isReady()
+{
+#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
+ return mSem != CTHSEMARG;
+#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
+ return !mToken.isNull();
+#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
+}
+
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+/** client token holder thread */
+DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD Thread, void *pvUser)
+{
+ LogFlowFuncEnter();
+
+ Assert(pvUser);
+
+ void **data = (void **)pvUser;
+
+# if defined(RT_OS_WINDOWS)
+ BSTR sessionId = (BSTR)data[0];
+ HANDLE initDoneSem = (HANDLE)data[1];
+
+ HANDLE mutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, sessionId);
+ AssertMsg(mutex, ("cannot open token, err=%d\n", ::GetLastError()));
+
+ if (mutex)
+ {
+ /* grab the token */
+ DWORD wrc = ::WaitForSingleObject(mutex, 0);
+ AssertMsg(wrc == WAIT_OBJECT_0, ("cannot grab token, err=%d\n", wrc));
+ if (wrc == WAIT_OBJECT_0)
+ {
+ HANDLE finishSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
+ AssertMsg(finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
+ if (finishSem)
+ {
+ data[2] = (void*)finishSem;
+ /* signal we're done with init */
+ ::SetEvent(initDoneSem);
+ /* wait until we're signaled to release the token */
+ ::WaitForSingleObject(finishSem, INFINITE);
+ /* release the token */
+ LogFlow(("ClientTokenHolderThread(): releasing token...\n"));
+ BOOL success = ::ReleaseMutex(mutex);
+ AssertMsg(success, ("cannot release token, err=%d\n", ::GetLastError()));
+ ::CloseHandle(mutex);
+ ::CloseHandle(finishSem);
+ }
+ }
+ }
+
+ /* signal we're done */
+ ::SetEvent(initDoneSem);
+# elif defined(RT_OS_OS2)
+ Utf8Str sessionId = (BSTR)data[0];
+ RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
+
+ LogFlowFunc(("sessionId='%s', finishSem=%p\n", sessionId.raw(), finishSem));
+
+ HMTX mutex = NULLHANDLE;
+ APIRET arc = ::DosOpenMutexSem((PSZ)sessionId.raw(), &mutex);
+ AssertMsg(arc == NO_ERROR, ("cannot open token, arc=%ld\n", arc));
+
+ if (arc == NO_ERROR)
+ {
+ /* grab the token */
+ LogFlowFunc(("grabbing token...\n"));
+ arc = ::DosRequestMutexSem(mutex, SEM_IMMEDIATE_RETURN);
+ AssertMsg(arc == NO_ERROR, ("cannot grab token, arc=%ld\n", arc));
+ if (arc == NO_ERROR)
+ {
+ /* store the answer */
+ data[2] = (void*)true;
+ /* signal we're done */
+ int vrc = RTThreadUserSignal(Thread);
+ AssertRC(vrc);
+
+ /* wait until we're signaled to release the token */
+ LogFlowFunc(("waiting for termination signal..\n"));
+ vrc = RTSemEventWait(finishSem, RT_INDEFINITE_WAIT);
+ Assert(arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
+
+ /* release the token */
+ LogFlowFunc(("releasing token...\n"));
+ arc = ::DosReleaseMutexSem(mutex);
+ AssertMsg(arc == NO_ERROR, ("cannot release token, arc=%ld\n", arc));
+ }
+ ::DosCloseMutexSem(mutex);
+ }
+
+ /* store the answer */
+ data[1] = (void*)false;
+ /* signal we're done */
+ int vrc = RTThreadUserSignal(Thread);
+ AssertRC(vrc);
+# else
+# error "Port me!"
+# endif
+
+ LogFlowFuncLeave();
+
+ return 0;
+}
+#endif
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */
diff --git a/src/VBox/Main/src-client/ConsoleImpl.cpp b/src/VBox/Main/src-client/ConsoleImpl.cpp
index 6c29a2dd..d0c74c24 100644
--- a/src/VBox/Main/src-client/ConsoleImpl.cpp
+++ b/src/VBox/Main/src-client/ConsoleImpl.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2005-2012 Oracle Corporation
+ * Copyright (C) 2005-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -57,19 +57,17 @@
#include "SharedFolderImpl.h"
#include "AudioSnifferInterface.h"
#include "Nvram.h"
-#ifdef VBOX_WITH_USB_VIDEO
-# include "UsbWebcamInterface.h"
-#endif
#ifdef VBOX_WITH_USB_CARDREADER
# include "UsbCardReader.h"
#endif
-#include "ProgressCombinedImpl.h"
+#include "ProgressImpl.h"
#include "ConsoleVRDPServer.h"
#include "VMMDev.h"
#ifdef VBOX_WITH_EXTPACK
# include "ExtPackManagerImpl.h"
#endif
#include "BusAssignmentManager.h"
+#include "EmulatedUSBImpl.h"
#include "VBoxEvents.h"
#include "AutoCaller.h"
@@ -155,7 +153,7 @@ struct VMTask
mConsoleCaller(aConsole),
mProgress(aProgress),
mServerProgress(aServerProgress),
- mpVM(NULL),
+ mpUVM(NULL),
mRC(E_FAIL),
mpSafeVMPtr(NULL)
{
@@ -167,7 +165,7 @@ struct VMTask
{
mpSafeVMPtr = new Console::SafeVMPtr(aConsole);
if (mpSafeVMPtr->isOk())
- mpVM = mpSafeVMPtr->raw();
+ mpUVM = mpSafeVMPtr->rawUVM();
else
mRC = mpSafeVMPtr->rc();
}
@@ -196,7 +194,7 @@ struct VMTask
const ComObjPtr<Progress> mProgress;
Utf8Str mErrorMsg;
const ComPtr<IProgress> mServerProgress;
- PVM mpVM;
+ PUVM mpUVM;
private:
HRESULT mRC;
@@ -262,16 +260,20 @@ struct VMSaveTask : public VMTask
VMSaveTask(Console *aConsole,
const ComPtr<IProgress> &aServerProgress,
const Utf8Str &aSavedStateFile,
- MachineState_T aMachineStateBefore)
+ MachineState_T aMachineStateBefore,
+ Reason_T aReason)
: VMTask(aConsole, NULL /* aProgress */, aServerProgress,
true /* aUsesVMPtr */),
mSavedStateFile(aSavedStateFile),
- mMachineStateBefore(aMachineStateBefore)
+ mMachineStateBefore(aMachineStateBefore),
+ mReason(aReason)
{}
Utf8Str mSavedStateFile;
/* The local machine state we had before. Required if something fails */
MachineState_T mMachineStateBefore;
+ /* The reason for saving state */
+ Reason_T mReason;
};
// Handler for global events
@@ -344,13 +346,34 @@ public:
break;
}
+ case VBoxEventType_OnExtraDataChanged:
+ {
+ ComPtr<IExtraDataChangedEvent> pEDCEv = aEvent;
+ Bstr strMachineId;
+ Bstr strKey;
+ Bstr strVal;
+ HRESULT hrc = S_OK;
+
+ hrc = pEDCEv->COMGETTER(MachineId)(strMachineId.asOutParam());
+ if (FAILED(hrc)) break;
+
+ hrc = pEDCEv->COMGETTER(Key)(strKey.asOutParam());
+ if (FAILED(hrc)) break;
+
+ hrc = pEDCEv->COMGETTER(Value)(strVal.asOutParam());
+ if (FAILED(hrc)) break;
+
+ mConsole->onExtraDataChange(strMachineId.raw(), strKey.raw(), strVal.raw());
+ break;
+ }
+
default:
AssertFailed();
}
return S_OK;
}
private:
- Console *mConsole;
+ ComObjPtr<Console> mConsole;
};
typedef ListenerImpl<VmEventListener, Console*> VmEventListenerImpl;
@@ -365,6 +388,8 @@ VBOX_LISTENER_DECLARE(VmEventListenerImpl)
Console::Console()
: mSavedStateDataLoaded(false)
, mConsoleVRDPServer(NULL)
+ , mfVRDEChangeInProcess(false)
+ , mfVRDEChangePending(false)
, mpUVM(NULL)
, mVMCallers(0)
, mVMZeroCallersSem(NIL_RTSEMEVENT)
@@ -374,13 +399,12 @@ Console::Console()
, mfSnapshotFolderSizeWarningShown(false)
, mfSnapshotFolderExt4WarningShown(false)
, mfSnapshotFolderDiskTypeShown(false)
+ , mfVMHasUsbController(false)
+ , mfPowerOffCausedByReset(false)
, mpVmm2UserMethods(NULL)
, m_pVMMDev(NULL)
, mAudioSniffer(NULL)
, mNvram(NULL)
-#ifdef VBOX_WITH_USB_VIDEO
- , mUsbWebcamInterface(NULL)
-#endif
#ifdef VBOX_WITH_USB_CARDREADER
, mUsbCardReader(NULL)
#endif
@@ -398,10 +422,10 @@ HRESULT Console::FinalConstruct()
{
LogFlowThisFunc(("\n"));
- memset(mapStorageLeds, 0, sizeof(mapStorageLeds));
- memset(mapNetworkLeds, 0, sizeof(mapNetworkLeds));
- memset(&mapUSBLed, 0, sizeof(mapUSBLed));
- memset(&mapSharedFolderLed, 0, sizeof(mapSharedFolderLed));
+ RT_ZERO(mapStorageLeds);
+ RT_ZERO(mapNetworkLeds);
+ RT_ZERO(mapUSBLed);
+ RT_ZERO(mapSharedFolderLed);
for (unsigned i = 0; i < RT_ELEMENTS(maStorageDevType); ++i)
maStorageDevType[i] = DeviceType_Null;
@@ -416,6 +440,7 @@ HRESULT Console::FinalConstruct()
pVmm2UserMethods->pfnNotifyEmtTerm = Console::vmm2User_NotifyEmtTerm;
pVmm2UserMethods->pfnNotifyPdmtInit = Console::vmm2User_NotifyPdmtInit;
pVmm2UserMethods->pfnNotifyPdmtTerm = Console::vmm2User_NotifyPdmtTerm;
+ pVmm2UserMethods->pfnNotifyResetTurnedIntoPowerOff = Console::vmm2User_NotifyResetTurnedIntoPowerOff;
pVmm2UserMethods->u32EndMagic = VMM2USERMETHODS_MAGIC;
pVmm2UserMethods->pConsole = this;
mpVmm2UserMethods = pVmm2UserMethods;
@@ -464,7 +489,7 @@ HRESULT Console::init(IMachine *aMachine, IInternalMachineControl *aControl, Loc
// Event source may be needed by other children
unconst(mEventSource).createObject();
- rc = mEventSource->init(static_cast<IConsole*>(this));
+ rc = mEventSource->init();
AssertComRCReturnRC(rc);
mcAudioRefs = 0;
@@ -498,6 +523,10 @@ HRESULT Console::init(IMachine *aMachine, IInternalMachineControl *aControl, Loc
rc = mVRDEServerInfo->init(this);
AssertComRCReturnRC(rc);
+ unconst(mEmulatedUSB).createObject();
+ rc = mEmulatedUSB->init(this);
+ AssertComRCReturnRC(rc);
+
/* Grab global and machine shared folder lists */
rc = fetchSharedFolders(true /* aGlobal */);
@@ -548,10 +577,6 @@ HRESULT Console::init(IMachine *aMachine, IInternalMachineControl *aControl, Loc
AssertReturn(mNvram, E_FAIL);
}
-#ifdef VBOX_WITH_USB_VIDEO
- unconst(mUsbWebcamInterface) = new UsbWebcamInterface(this);
- AssertReturn(mUsbWebcamInterface, E_FAIL);
-#endif
#ifdef VBOX_WITH_USB_CARDREADER
unconst(mUsbCardReader) = new UsbCardReader(this);
AssertReturn(mUsbCardReader, E_FAIL);
@@ -569,6 +594,7 @@ HRESULT Console::init(IMachine *aMachine, IInternalMachineControl *aControl, Loc
com::SafeArray<VBoxEventType_T> eventTypes;
eventTypes.push_back(VBoxEventType_OnNATRedirect);
eventTypes.push_back(VBoxEventType_OnHostPCIDevicePlug);
+ eventTypes.push_back(VBoxEventType_OnExtraDataChanged);
rc = pES->RegisterListener(aVmListener, ComSafeArrayAsInParam(eventTypes), true);
AssertComRC(rc);
}
@@ -649,14 +675,6 @@ void Console::uninit()
unconst(mNvram) = NULL;
}
-#ifdef VBOX_WITH_USB_VIDEO
- if (mUsbWebcamInterface)
- {
- delete mUsbWebcamInterface;
- unconst(mUsbWebcamInterface) = NULL;
- }
-#endif
-
#ifdef VBOX_WITH_USB_CARDREADER
if (mUsbCardReader)
{
@@ -697,6 +715,12 @@ void Console::uninit()
unconst(mVRDEServerInfo).setNull();
}
+ if (mEmulatedUSB)
+ {
+ mEmulatedUSB->uninit();
+ unconst(mEmulatedUSB).setNull();
+ }
+
if (mDebugger)
{
mDebugger->uninit();
@@ -778,8 +802,7 @@ void Console::guestPropertiesHandleVMReset(void)
/* Delete all properties which have the flag "TRANSRESET". */
if (Utf8Str(arrFlags[i]).contains("TRANSRESET", Utf8Str::CaseInsensitive))
{
- hrc = mMachine->SetGuestProperty(arrNames[i], Bstr("").raw() /* Value */,
- Bstr("").raw() /* Flags */);
+ hrc = mMachine->DeleteGuestProperty(arrNames[i]);
if (FAILED(hrc))
LogRel(("RESET: Could not delete transient property \"%ls\", rc=%Rhrc\n",
arrNames[i], hrc));
@@ -989,6 +1012,17 @@ void Console::guestPropertiesVRDPUpdateDisconnect(uint32_t u32ClientId)
#endif /* VBOX_WITH_GUEST_PROPS */
+bool Console::isResetTurnedIntoPowerOff(void)
+{
+ Bstr value;
+ HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/TurnResetIntoPowerOff").raw(),
+ value.asOutParam());
+ if ( hrc == S_OK
+ && value == "1")
+ return true;
+ return false;
+}
+
#ifdef VBOX_WITH_EXTPACK
/**
* Used by VRDEServer and others to talke to the extension pack manager.
@@ -1229,9 +1263,13 @@ int Console::VRDPClientLogon(uint32_t u32ClientId, const char *pszUser, const ch
{
uint32_t u32GuestFlags = VMMDEV_SETCREDENTIALS_GUESTLOGON;
- int rc = m_pVMMDev->getVMMDevPort()->pfnSetCredentials(m_pVMMDev->getVMMDevPort(),
- pszUser, pszPassword, pszDomain, u32GuestFlags);
- AssertRC(rc);
+ PPDMIVMMDEVPORT pDevPort = m_pVMMDev->getVMMDevPort();
+ if (pDevPort)
+ {
+ int rc = pDevPort->pfnSetCredentials(m_pVMMDev->getVMMDevPort(),
+ pszUser, pszPassword, pszDomain, u32GuestFlags);
+ AssertRC(rc);
+ }
}
return VINF_SUCCESS;
@@ -1697,8 +1735,8 @@ DECLCALLBACK(int) Console::doGuestPropNotification(void *pvExtension,
PHOSTCALLBACKDATA pCBData = reinterpret_cast<PHOSTCALLBACKDATA>(pvParms);
AssertReturn(sizeof(HOSTCALLBACKDATA) == cbParms, VERR_INVALID_PARAMETER);
AssertReturn(HOSTCALLBACKMAGIC == pCBData->u32Magic, VERR_INVALID_PARAMETER);
- Log5(("Console::doGuestPropNotification: pCBData={.pcszName=%s, .pcszValue=%s, .pcszFlags=%s}\n",
- pCBData->pcszName, pCBData->pcszValue, pCBData->pcszFlags));
+ LogFlow(("Console::doGuestPropNotification: pCBData={.pcszName=%s, .pcszValue=%s, .pcszFlags=%s}\n",
+ pCBData->pcszName, pCBData->pcszValue, pCBData->pcszFlags));
int rc;
Bstr name(pCBData->pcszName);
@@ -1713,7 +1751,7 @@ DECLCALLBACK(int) Console::doGuestPropNotification(void *pvExtension,
rc = VINF_SUCCESS;
else
{
- LogFunc(("Console::doGuestPropNotification: hrc=%Rhrc pCBData={.pcszName=%s, .pcszValue=%s, .pcszFlags=%s}\n",
+ LogFlow(("Console::doGuestPropNotification: hrc=%Rhrc pCBData={.pcszName=%s, .pcszValue=%s, .pcszFlags=%s}\n",
hrc, pCBData->pcszName, pCBData->pcszValue, pCBData->pcszFlags));
rc = Global::vboxStatusCodeFromCOM(hrc);
}
@@ -1968,12 +2006,25 @@ STDMETHODIMP Console::COMGETTER(VRDEServerInfo)(IVRDEServerInfo **aVRDEServerInf
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- /* mDisplay is constant during life time, no need to lock */
+ /* mVRDEServerInfo is constant during life time, no need to lock */
mVRDEServerInfo.queryInterfaceTo(aVRDEServerInfo);
return S_OK;
}
+STDMETHODIMP Console::COMGETTER(EmulatedUSB)(IEmulatedUSB **aEmulatedUSB)
+{
+ CheckComArgOutPointerValid(aEmulatedUSB);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ /* mEmulatedUSB is constant during life time, no need to lock */
+ mEmulatedUSB.queryInterfaceTo(aEmulatedUSB);
+
+ return S_OK;
+}
+
STDMETHODIMP
Console::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
{
@@ -2074,7 +2125,6 @@ STDMETHODIMP Console::PowerUpPaused(IProgress **aProgress)
STDMETHODIMP Console::PowerDown(IProgress **aProgress)
{
LogFlowThisFuncEnter();
- LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
CheckComArgOutPointerValid(aProgress);
@@ -2083,6 +2133,7 @@ STDMETHODIMP Console::PowerDown(IProgress **aProgress)
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
switch (mMachineState)
{
case MachineState_Running:
@@ -2144,6 +2195,20 @@ STDMETHODIMP Console::PowerDown(IProgress **aProgress)
{
ComPtr<IProgress> pProgress;
+#ifdef VBOX_WITH_GUEST_PROPS
+ alock.release();
+
+ if (isResetTurnedIntoPowerOff())
+ {
+ mMachine->DeleteGuestProperty(Bstr("/VirtualBox/HostInfo/VMPowerOffReason").raw());
+ mMachine->SetGuestProperty(Bstr("/VirtualBox/HostInfo/VMPowerOffReason").raw(),
+ Bstr("PowerOff").raw(), Bstr("RDONLYGUEST").raw());
+ mMachine->SaveSettings();
+ }
+
+ alock.acquire();
+#endif
+
/*
* request a progress object from the server
* (this will set the machine state to Stopping on the server to block
@@ -2206,13 +2271,13 @@ STDMETHODIMP Console::PowerDown(IProgress **aProgress)
STDMETHODIMP Console::Reset()
{
LogFlowThisFuncEnter();
- LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Teleporting
&& mMachineState != MachineState_LiveSnapshotting
@@ -2228,7 +2293,7 @@ STDMETHODIMP Console::Reset()
/* release the lock before a VMR3* call (EMT will call us back)! */
alock.release();
- int vrc = VMR3Reset(ptrVM);
+ int vrc = VMR3Reset(ptrVM.rawUVM());
HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
setError(VBOX_E_VM_ERROR,
@@ -2240,30 +2305,30 @@ STDMETHODIMP Console::Reset()
return rc;
}
-/*static*/ DECLCALLBACK(int) Console::unplugCpu(Console *pThis, PVM pVM, unsigned uCpu)
+/*static*/ DECLCALLBACK(int) Console::unplugCpu(Console *pThis, PUVM pUVM, VMCPUID idCpu)
{
- LogFlowFunc(("pThis=%p pVM=%p uCpu=%u\n", pThis, pVM, uCpu));
+ LogFlowFunc(("pThis=%p pVM=%p idCpu=%u\n", pThis, pUVM, idCpu));
AssertReturn(pThis, VERR_INVALID_PARAMETER);
- int vrc = PDMR3DeviceDetach(pVM, "acpi", 0, uCpu, 0);
+ int vrc = PDMR3DeviceDetach(pUVM, "acpi", 0, idCpu, 0);
Log(("UnplugCpu: rc=%Rrc\n", vrc));
return vrc;
}
-HRESULT Console::doCPURemove(ULONG aCpu, PVM pVM)
+HRESULT Console::doCPURemove(ULONG aCpu, PUVM pUVM)
{
HRESULT rc = S_OK;
LogFlowThisFuncEnter();
- LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
AssertReturn(m_pVMMDev, E_FAIL);
PPDMIVMMDEVPORT pVmmDevPort = m_pVMMDev->getVMMDevPort();
AssertReturn(pVmmDevPort, E_FAIL);
@@ -2288,7 +2353,7 @@ HRESULT Console::doCPURemove(ULONG aCpu, PVM pVM)
/* Check if the CPU is unlocked */
PPDMIBASE pBase;
- int vrc = PDMR3QueryDeviceLun(pVM, "acpi", 0, aCpu, &pBase);
+ int vrc = PDMR3QueryDeviceLun(pUVM, "acpi", 0, aCpu, &pBase);
if (RT_SUCCESS(vrc))
{
Assert(pBase);
@@ -2296,7 +2361,7 @@ HRESULT Console::doCPURemove(ULONG aCpu, PVM pVM)
/* Notify the guest if possible. */
uint32_t idCpuCore, idCpuPackage;
- vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(pVM, aCpu, &idCpuCore, &idCpuPackage); AssertRC(vrc);
+ vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(pUVM, aCpu, &idCpuCore, &idCpuPackage); AssertRC(vrc);
if (RT_SUCCESS(vrc))
vrc = pVmmDevPort->pfnCpuHotUnplug(pVmmDevPort, idCpuCore, idCpuPackage);
if (RT_SUCCESS(vrc))
@@ -2328,9 +2393,9 @@ HRESULT Console::doCPURemove(ULONG aCpu, PVM pVM)
* using VMR3ReqCall.
*/
PVMREQ pReq;
- vrc = VMR3ReqCall(pVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
- (PFNRT)Console::unplugCpu, 3,
- this, pVM, aCpu);
+ vrc = VMR3ReqCallU(pUVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
+ (PFNRT)unplugCpu, 3,
+ this, pUVM, (VMCPUID)aCpu);
if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
{
vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
@@ -2343,7 +2408,7 @@ HRESULT Console::doCPURemove(ULONG aCpu, PVM pVM)
if (RT_SUCCESS(vrc))
{
/* Detach it from the VM */
- vrc = VMR3HotUnplugCpu(pVM, aCpu);
+ vrc = VMR3HotUnplugCpu(pUVM, aCpu);
AssertRC(vrc);
}
else
@@ -2359,25 +2424,25 @@ HRESULT Console::doCPURemove(ULONG aCpu, PVM pVM)
return rc;
}
-/*static*/ DECLCALLBACK(int) Console::plugCpu(Console *pThis, PVM pVM, unsigned uCpu)
+/*static*/ DECLCALLBACK(int) Console::plugCpu(Console *pThis, PUVM pUVM, VMCPUID idCpu)
{
- LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, uCpu));
+ LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, idCpu));
AssertReturn(pThis, VERR_INVALID_PARAMETER);
- int rc = VMR3HotPlugCpu(pVM, uCpu);
+ int rc = VMR3HotPlugCpu(pUVM, idCpu);
AssertRC(rc);
- PCFGMNODE pInst = CFGMR3GetChild(CFGMR3GetRoot(pVM), "Devices/acpi/0/");
+ PCFGMNODE pInst = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "Devices/acpi/0/");
AssertRelease(pInst);
/* nuke anything which might have been left behind. */
- CFGMR3RemoveNode(CFGMR3GetChildF(pInst, "LUN#%d", uCpu));
+ CFGMR3RemoveNode(CFGMR3GetChildF(pInst, "LUN#%u", idCpu));
#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; } } while (0)
PCFGMNODE pLunL0;
PCFGMNODE pCfg;
- rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%d", uCpu); RC_CHECK();
+ rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%u", idCpu); RC_CHECK();
rc = CFGMR3InsertString(pLunL0, "Driver", "ACPICpu"); RC_CHECK();
rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
@@ -2385,7 +2450,7 @@ HRESULT Console::doCPURemove(ULONG aCpu, PVM pVM)
* Attach the driver.
*/
PPDMIBASE pBase;
- rc = PDMR3DeviceAttach(pVM, "acpi", 0, uCpu, 0, &pBase); RC_CHECK();
+ rc = PDMR3DeviceAttach(pUVM, "acpi", 0, idCpu, 0, &pBase); RC_CHECK();
Log(("PlugCpu: rc=%Rrc\n", rc));
@@ -2396,18 +2461,18 @@ HRESULT Console::doCPURemove(ULONG aCpu, PVM pVM)
return VINF_SUCCESS;
}
-HRESULT Console::doCPUAdd(ULONG aCpu, PVM pVM)
+HRESULT Console::doCPUAdd(ULONG aCpu, PUVM pUVM)
{
HRESULT rc = S_OK;
LogFlowThisFuncEnter();
- LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Teleporting
&& mMachineState != MachineState_LiveSnapshotting
@@ -2434,9 +2499,9 @@ HRESULT Console::doCPUAdd(ULONG aCpu, PVM pVM)
* here to make requests from under the lock in order to serialize them.
*/
PVMREQ pReq;
- int vrc = VMR3ReqCall(pVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
- (PFNRT)Console::plugCpu, 3,
- this, pVM, aCpu);
+ int vrc = VMR3ReqCallU(pUVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
+ (PFNRT)plugCpu, 3,
+ this, pUVM, aCpu);
/* release the lock before a VMR3* call (EMT will call us back)! */
alock.release();
@@ -2459,7 +2524,7 @@ HRESULT Console::doCPUAdd(ULONG aCpu, PVM pVM)
{
/* Notify the guest if possible. */
uint32_t idCpuCore, idCpuPackage;
- vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(pVM, aCpu, &idCpuCore, &idCpuPackage); AssertRC(vrc);
+ vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(pUVM, aCpu, &idCpuCore, &idCpuPackage); AssertRC(vrc);
if (RT_SUCCESS(vrc))
vrc = pDevPort->pfnCpuHotPlug(pDevPort, idCpuCore, idCpuPackage);
/** @todo warning if the guest doesn't support it */
@@ -2474,89 +2539,18 @@ STDMETHODIMP Console::Pause()
{
LogFlowThisFuncEnter();
- AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
- switch (mMachineState)
- {
- case MachineState_Running:
- case MachineState_Teleporting:
- case MachineState_LiveSnapshotting:
- break;
-
- case MachineState_Paused:
- case MachineState_TeleportingPausedVM:
- case MachineState_Saving:
- return setError(VBOX_E_INVALID_VM_STATE, tr("Already paused"));
-
- default:
- return setInvalidMachineStateError();
- }
+ HRESULT rc = pause(Reason_Unspecified);
- /* get the VM handle. */
- SafeVMPtr ptrVM(this);
- if (!ptrVM.isOk())
- return ptrVM.rc();
-
- LogFlowThisFunc(("Sending PAUSE request...\n"));
-
- /* release the lock before a VMR3* call (EMT will call us back)! */
- alock.release();
-
- int vrc = VMR3Suspend(ptrVM);
-
- HRESULT hrc = S_OK;
- if (RT_FAILURE(vrc))
- hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
-
- LogFlowThisFunc(("hrc=%Rhrc\n", hrc));
+ LogFlowThisFunc(("rc=%Rhrc\n", rc));
LogFlowThisFuncLeave();
- return hrc;
+ return rc;
}
STDMETHODIMP Console::Resume()
{
LogFlowThisFuncEnter();
- AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
- if (mMachineState != MachineState_Paused)
- return setError(VBOX_E_INVALID_VM_STATE,
- tr("Cannot resume the machine as it is not paused (machine state: %s)"),
- Global::stringifyMachineState(mMachineState));
-
- /* get the VM handle. */
- SafeVMPtr ptrVM(this);
- if (!ptrVM.isOk())
- return ptrVM.rc();
-
- LogFlowThisFunc(("Sending RESUME request...\n"));
-
- /* release the lock before a VMR3* call (EMT will call us back)! */
- alock.release();
-
-#ifdef VBOX_WITH_EXTPACK
- int vrc = mptrExtPackManager->callAllVmPowerOnHooks(this, ptrVM); /** @todo called a few times too many... */
-#else
- int vrc = VINF_SUCCESS;
-#endif
- if (RT_SUCCESS(vrc))
- {
- if (VMR3GetState(ptrVM) == VMSTATE_CREATED)
- vrc = VMR3PowerOn(ptrVM); /* (PowerUpPaused) */
- else
- vrc = VMR3Resume(ptrVM);
- }
-
- HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
- setError(VBOX_E_VM_ERROR,
- tr("Could not resume the machine execution (%Rrc)"),
- vrc);
+ HRESULT rc = resume(Reason_Unspecified);
LogFlowThisFunc(("rc=%Rhrc\n", rc));
LogFlowThisFuncLeave();
@@ -2587,7 +2581,7 @@ STDMETHODIMP Console::PowerButton()
/* get the acpi device interface and press the button. */
PPDMIBASE pBase;
- int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
+ int vrc = PDMR3QueryDeviceLun(ptrVM.rawUVM(), "acpi", 0, 0, &pBase);
if (RT_SUCCESS(vrc))
{
Assert(pBase);
@@ -2635,7 +2629,7 @@ STDMETHODIMP Console::GetPowerButtonHandled(BOOL *aHandled)
/* get the acpi device interface and check if the button press was handled. */
PPDMIBASE pBase;
- int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
+ int vrc = PDMR3QueryDeviceLun(ptrVM.rawUVM(), "acpi", 0, 0, &pBase);
if (RT_SUCCESS(vrc))
{
Assert(pBase);
@@ -2690,7 +2684,7 @@ STDMETHODIMP Console::GetGuestEnteredACPIMode(BOOL *aEntered)
/* get the acpi device interface and query the information. */
PPDMIBASE pBase;
- int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
+ int vrc = PDMR3QueryDeviceLun(ptrVM.rawUVM(), "acpi", 0, 0, &pBase);
if (RT_SUCCESS(vrc))
{
Assert(pBase);
@@ -2719,7 +2713,9 @@ STDMETHODIMP Console::SleepButton()
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- if (mMachineState != MachineState_Running) /** @todo Live Migration: ??? */
+ if ( mMachineState != MachineState_Running
+ && mMachineState != MachineState_Teleporting
+ && mMachineState != MachineState_LiveSnapshotting)
return setInvalidMachineStateError();
/* get the VM handle. */
@@ -2731,7 +2727,7 @@ STDMETHODIMP Console::SleepButton()
/* get the acpi device interface and press the sleep button. */
PPDMIBASE pBase;
- int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
+ int vrc = PDMR3QueryDeviceLun(ptrVM.rawUVM(), "acpi", 0, 0, &pBase);
if (RT_SUCCESS(vrc))
{
Assert(pBase);
@@ -2755,150 +2751,8 @@ STDMETHODIMP Console::SleepButton()
STDMETHODIMP Console::SaveState(IProgress **aProgress)
{
LogFlowThisFuncEnter();
- LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
-
- CheckComArgOutPointerValid(aProgress);
-
- AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
- if ( mMachineState != MachineState_Running
- && mMachineState != MachineState_Paused)
- {
- return setError(VBOX_E_INVALID_VM_STATE,
- tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
- Global::stringifyMachineState(mMachineState));
- }
-
- /* memorize the current machine state */
- MachineState_T lastMachineState = mMachineState;
-
- if (mMachineState == MachineState_Running)
- {
- /* get the VM handle. */
- SafeVMPtr ptrVM(this);
- if (!ptrVM.isOk())
- return ptrVM.rc();
-
- /* release the lock before a VMR3* call (EMT will call us back)! */
- alock.release();
- int vrc = VMR3Suspend(ptrVM);
- alock.acquire();
-
- HRESULT hrc = S_OK;
- if (RT_FAILURE(vrc))
- hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
- if (FAILED(hrc))
- return hrc;
- }
-
- HRESULT rc = S_OK;
- bool fBeganSavingState = false;
- bool fTaskCreationFailed = false;
-
- do
- {
- ComPtr<IProgress> pProgress;
- Bstr stateFilePath;
-
- /*
- * request a saved state file path from the server
- * (this will set the machine state to Saving on the server to block
- * others from accessing this machine)
- */
- rc = mControl->BeginSavingState(pProgress.asOutParam(),
- stateFilePath.asOutParam());
- if (FAILED(rc))
- break;
-
- fBeganSavingState = true;
-
- /* sync the state with the server */
- setMachineStateLocally(MachineState_Saving);
-
- /* ensure the directory for the saved state file exists */
- {
- Utf8Str dir = stateFilePath;
- dir.stripFilename();
- if (!RTDirExists(dir.c_str()))
- {
- int vrc = RTDirCreateFullPath(dir.c_str(), 0700);
- if (RT_FAILURE(vrc))
- {
- rc = setError(VBOX_E_FILE_ERROR,
- tr("Could not create a directory '%s' to save the state to (%Rrc)"),
- dir.c_str(), vrc);
- break;
- }
- }
- }
-
- /* create a task object early to ensure mpVM protection is successful */
- std::auto_ptr<VMSaveTask> task(new VMSaveTask(this, pProgress,
- stateFilePath,
- lastMachineState));
- rc = task->rc();
- /*
- * If we fail here it means a PowerDown() call happened on another
- * thread while we were doing Pause() (which releases the Console lock).
- * We assign PowerDown() a higher precedence than SaveState(),
- * therefore just return the error to the caller.
- */
- if (FAILED(rc))
- {
- fTaskCreationFailed = true;
- break;
- }
-
- /* create a thread to wait until the VM state is saved */
- int vrc = RTThreadCreate(NULL, Console::saveStateThread, (void *)task.get(),
- 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave");
- if (RT_FAILURE(vrc))
- {
- rc = setError(E_FAIL, "Could not create VMSave thread (%Rrc)", vrc);
- break;
- }
- /* task is now owned by saveStateThread(), so release it */
- task.release();
-
- /* return the progress to the caller */
- pProgress.queryInterfaceTo(aProgress);
- } while (0);
-
- if (FAILED(rc) && !fTaskCreationFailed)
- {
- /* preserve existing error info */
- ErrorInfoKeeper eik;
-
- if (fBeganSavingState)
- {
- /*
- * cancel the requested save state procedure.
- * This will reset the machine state to the state it had right
- * before calling mControl->BeginSavingState().
- */
- mControl->EndSavingState(eik.getResultCode(), eik.getText().raw());
- }
-
- if (lastMachineState == MachineState_Running)
- {
- /* restore the paused state if appropriate */
- setMachineStateLocally(MachineState_Paused);
- /* restore the running state if appropriate */
- SafeVMPtr ptrVM(this);
- if (ptrVM.isOk())
- {
- alock.release();
- VMR3Resume(ptrVM);
- alock.acquire();
- }
- }
- else
- setMachineStateLocally(lastMachineState);
- }
+ HRESULT rc = saveState(Reason_Unspecified, aProgress);
LogFlowThisFunc(("rc=%Rhrc\n", rc));
LogFlowThisFuncLeave();
@@ -3049,10 +2903,8 @@ STDMETHODIMP Console::AttachUSBDevice(IN_BSTR aId)
if (!ptrVM.isOk())
return ptrVM.rc();
- /* Don't proceed unless we've found the usb controller. */
- PPDMIBASE pBase = NULL;
- int vrc = PDMR3QueryLun(ptrVM, "usb-ohci", 0, 0, &pBase);
- if (RT_FAILURE(vrc))
+ /* Don't proceed unless we have a USB controller. */
+ if (!mfVMHasUsbController)
return setError(VBOX_E_PDM_ERROR,
tr("The virtual machine does not have a USB controller"));
@@ -3176,7 +3028,7 @@ STDMETHODIMP Console::FindUSBDeviceByAddress(IN_BSTR aAddress, IUSBDevice **aDev
STDMETHODIMP Console::FindUSBDeviceById(IN_BSTR aId, IUSBDevice **aDevice)
{
#ifdef VBOX_WITH_USB
- CheckComArgExpr(aId, Guid(aId).isEmpty() == false);
+ CheckComArgExpr(aId, Guid(aId).isValid());
CheckComArgOutPointerValid(aDevice);
*aDevice = NULL;
@@ -3359,7 +3211,6 @@ STDMETHODIMP Console::TakeSnapshot(IN_BSTR aName,
IProgress **aProgress)
{
LogFlowThisFuncEnter();
- LogFlowThisFunc(("aName='%ls' mMachineState=%d\n", aName, mMachineState));
CheckComArgStrNotEmptyOrNull(aName);
CheckComArgOutPointerValid(aProgress);
@@ -3368,6 +3219,7 @@ STDMETHODIMP Console::TakeSnapshot(IN_BSTR aName,
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ LogFlowThisFunc(("aName='%ls' mMachineState=%d\n", aName, mMachineState));
if (Global::IsTransient(mMachineState))
return setError(VBOX_E_INVALID_VM_STATE,
@@ -3488,7 +3340,7 @@ STDMETHODIMP Console::TakeSnapshot(IN_BSTR aName,
STDMETHODIMP Console::DeleteSnapshot(IN_BSTR aId, IProgress **aProgress)
{
- CheckComArgExpr(aId, Guid(aId).isEmpty() == false);
+ CheckComArgExpr(aId, Guid(aId).isValid());
CheckComArgOutPointerValid(aProgress);
AutoCaller autoCaller(this);
@@ -3511,7 +3363,7 @@ STDMETHODIMP Console::DeleteSnapshot(IN_BSTR aId, IProgress **aProgress)
STDMETHODIMP Console::DeleteSnapshotAndAllChildren(IN_BSTR aId, IProgress **aProgress)
{
- CheckComArgExpr(aId, Guid(aId).isEmpty() == false);
+ CheckComArgExpr(aId, Guid(aId).isValid());
CheckComArgOutPointerValid(aProgress);
AutoCaller autoCaller(this);
@@ -3534,8 +3386,8 @@ STDMETHODIMP Console::DeleteSnapshotAndAllChildren(IN_BSTR aId, IProgress **aPro
STDMETHODIMP Console::DeleteSnapshotRange(IN_BSTR aStartId, IN_BSTR aEndId, IProgress **aProgress)
{
- CheckComArgExpr(aStartId, Guid(aStartId).isEmpty() == false);
- CheckComArgExpr(aEndId, Guid(aEndId).isEmpty() == false);
+ CheckComArgExpr(aStartId, Guid(aStartId).isValid());
+ CheckComArgExpr(aEndId, Guid(aEndId).isValid());
CheckComArgOutPointerValid(aProgress);
AutoCaller autoCaller(this);
@@ -3653,17 +3505,94 @@ HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG
// private methods
/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Suspend the VM before we do any medium or network attachment change.
+ *
+ * @param pUVM Safe VM handle.
+ * @param pAlock The automatic lock instance. This is for when we have
+ * to leave it in order to avoid deadlocks.
+ * @param pfSuspend where to store the information if we need to resume
+ * afterwards.
+ */
+HRESULT Console::suspendBeforeConfigChange(PUVM pUVM, AutoWriteLock *pAlock, bool *pfResume)
+{
+ *pfResume = false;
+ VMSTATE enmVMState = VMR3GetStateU(pUVM);
+ switch (enmVMState)
+ {
+ case VMSTATE_RESETTING:
+ case VMSTATE_RUNNING:
+ {
+ LogFlowFunc(("Suspending the VM...\n"));
+ /* disable the callback to prevent Console-level state change */
+ mVMStateChangeCallbackDisabled = true;
+ if (pAlock)
+ pAlock->release();
+ int rc = VMR3Suspend(pUVM, VMSUSPENDREASON_RECONFIG);
+ if (pAlock)
+ pAlock->acquire();
+ mVMStateChangeCallbackDisabled = false;
+ if (RT_FAILURE(rc))
+ return setErrorInternal(VBOX_E_INVALID_VM_STATE,
+ COM_IIDOF(IConsole),
+ getStaticComponentName(),
+ Utf8StrFmt("Couldn't suspend VM for medium change (%Rrc)", rc),
+ false /*aWarning*/,
+ true /*aLogIt*/);
+ *pfResume = true;
+ break;
+ }
+ case VMSTATE_SUSPENDED:
+ break;
+ default:
+ return setErrorInternal(VBOX_E_INVALID_VM_STATE,
+ COM_IIDOF(IConsole),
+ getStaticComponentName(),
+ Utf8StrFmt("Invalid VM state '%s' for changing medium",
+ VMR3GetStateName(enmVMState)),
+ false /*aWarning*/,
+ true /*aLogIt*/);
+ }
+
+ return S_OK;
+}
+
+/**
+ * Resume the VM after we did any medium or network attachment change.
+ * This is the counterpart to Console::suspendBeforeConfigChange().
+ *
+ * @param pUVM Safe VM handle.
+ */
+void Console::resumeAfterConfigChange(PUVM pUVM)
+{
+ LogFlowFunc(("Resuming the VM...\n"));
+ /* disable the callback to prevent Console-level state change */
+ mVMStateChangeCallbackDisabled = true;
+ int rc = VMR3Resume(pUVM, VMRESUMEREASON_RECONFIG);
+ mVMStateChangeCallbackDisabled = false;
+ AssertRC(rc);
+ if (RT_FAILURE(rc))
+ {
+ VMSTATE enmVMState = VMR3GetStateU(pUVM);
+ if (enmVMState == VMSTATE_SUSPENDED)
+ {
+ /* too bad, we failed. try to sync the console state with the VMM state */
+ vmstateChangeCallback(pUVM, VMSTATE_SUSPENDED, enmVMState, this);
+ }
+ }
+}
/**
* Process a medium change.
*
* @param aMediumAttachment The medium attachment with the new medium state.
* @param fForce Force medium chance, if it is locked or not.
- * @param pVM Safe VM handle.
+ * @param pUVM Safe VM handle.
*
* @note Locks this object for writing.
*/
-HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce, PVM pVM)
+HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce, PUVM pUVM)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
@@ -3722,26 +3651,23 @@ HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForc
AssertComRC(rc);
/*
+ * Suspend the VM first. The VM must not be running since it might have
+ * pending I/O to the drive which is being changed.
+ */
+ bool fResume = false;
+ rc = suspendBeforeConfigChange(pUVM, &alock, &fResume);
+ if (FAILED(rc))
+ return rc;
+
+ /*
* Call worker in EMT, that's faster and safer than doing everything
* using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
* here to make requests from under the lock in order to serialize them.
*/
PVMREQ pReq;
- int vrc = VMR3ReqCall(pVM,
- VMCPUID_ANY,
- &pReq,
- 0 /* no wait! */,
- VMREQFLAGS_VBOX_STATUS,
- (PFNRT)Console::changeRemovableMedium,
- 8,
- this,
- pVM,
- pszDevice,
- uInstance,
- enmBus,
- fUseHostIOCache,
- aMediumAttachment,
- fForce);
+ int vrc = VMR3ReqCallU(pUVM, VMCPUID_ANY, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
+ (PFNRT)changeRemovableMedium, 8,
+ this, pUVM, pszDevice, uInstance, enmBus, fUseHostIOCache, aMediumAttachment, fForce);
/* release the lock before waiting for a result (EMT will call us back!) */
alock.release();
@@ -3755,6 +3681,9 @@ HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForc
}
VMR3ReqFree(pReq);
+ if (fResume)
+ resumeAfterConfigChange(pUVM);
+
if (RT_SUCCESS(vrc))
{
LogFlowThisFunc(("Returns S_OK\n"));
@@ -3777,7 +3706,7 @@ HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForc
* @returns VBox status code.
*
* @param pThis Pointer to the Console object.
- * @param pVM The VM handle.
+ * @param pUVM The VM handle.
* @param pcszDevice The PDM device name.
* @param uInstance The PDM device instance.
* @param uLun The PDM LUN number of the drive.
@@ -3790,9 +3719,10 @@ HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForc
* @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
*
* @thread EMT
+ * @note The VM must not be running since it might have pending I/O to the drive which is being changed.
*/
-DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole,
- PVM pVM,
+DECLCALLBACK(int) Console::changeRemovableMedium(Console *pThis,
+ PUVM pUVM,
const char *pcszDevice,
unsigned uInstance,
StorageBus_T enmBus,
@@ -3800,112 +3730,49 @@ DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole,
IMediumAttachment *aMediumAtt,
bool fForce)
{
- LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p, fForce=%d\n",
- pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt, fForce));
+ LogFlowFunc(("pThis=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p, fForce=%d\n",
+ pThis, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt, fForce));
- AssertReturn(pConsole, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis, VERR_INVALID_PARAMETER);
- AutoCaller autoCaller(pConsole);
+ AutoCaller autoCaller(pThis);
AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
/*
- * Suspend the VM first.
- *
- * The VM must not be running since it might have pending I/O to
- * the drive which is being changed.
+ * Check the VM for correct state.
*/
- bool fResume;
- VMSTATE enmVMState = VMR3GetState(pVM);
- switch (enmVMState)
- {
- case VMSTATE_RESETTING:
- case VMSTATE_RUNNING:
- {
- LogFlowFunc(("Suspending the VM...\n"));
- /* disable the callback to prevent Console-level state change */
- pConsole->mVMStateChangeCallbackDisabled = true;
- int rc = VMR3Suspend(pVM);
- pConsole->mVMStateChangeCallbackDisabled = false;
- AssertRCReturn(rc, rc);
- fResume = true;
- break;
- }
-
- case VMSTATE_SUSPENDED:
- case VMSTATE_CREATED:
- case VMSTATE_OFF:
- fResume = false;
- break;
-
- case VMSTATE_RUNNING_LS:
- case VMSTATE_RUNNING_FT:
- return setErrorInternal(VBOX_E_INVALID_VM_STATE,
- COM_IIDOF(IConsole),
- getStaticComponentName(),
- (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
- false /*aWarning*/,
- true /*aLogIt*/);
-
- default:
- AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
- }
+ VMSTATE enmVMState = VMR3GetStateU(pUVM);
+ AssertReturn(enmVMState == VMSTATE_SUSPENDED, VERR_INVALID_STATE);
/* Determine the base path for the device instance. */
PCFGMNODE pCtlInst;
- pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
+ pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
- int rc = VINF_SUCCESS;
- int rcRet = VINF_SUCCESS;
-
- rcRet = pConsole->configMediumAttachment(pCtlInst,
- pcszDevice,
- uInstance,
- enmBus,
- fUseHostIOCache,
- false /* fSetupMerge */,
- false /* fBuiltinIOCache */,
- 0 /* uMergeSource */,
- 0 /* uMergeTarget */,
- aMediumAtt,
- pConsole->mMachineState,
- NULL /* phrc */,
- true /* fAttachDetach */,
- fForce /* fForceUnmount */,
- false /* fHotplug */,
- pVM,
- NULL /* paLedDevType */);
- /** @todo this dumps everything attached to this device instance, which
- * is more than necessary. Dumping the changed LUN would be enough. */
- CFGMR3Dump(pCtlInst);
-
- /*
- * Resume the VM if necessary.
- */
- if (fResume)
- {
- LogFlowFunc(("Resuming the VM...\n"));
- /* disable the callback to prevent Console-level state change */
- pConsole->mVMStateChangeCallbackDisabled = true;
- rc = VMR3Resume(pVM);
- pConsole->mVMStateChangeCallbackDisabled = false;
- AssertRC(rc);
- if (RT_FAILURE(rc))
- {
- /* too bad, we failed. try to sync the console state with the VMM state */
- vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
- }
- /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
- // error (if any) will be hidden from the caller. For proper reporting
- // of such multiple errors to the caller we need to enhance the
- // IVirtualBoxError interface. For now, give the first error the higher
- // priority.
- if (RT_SUCCESS(rcRet))
- rcRet = rc;
- }
-
- LogFlowFunc(("Returning %Rrc\n", rcRet));
- return rcRet;
+ PCFGMNODE pLunL0 = NULL;
+ int rc = pThis->configMediumAttachment(pCtlInst,
+ pcszDevice,
+ uInstance,
+ enmBus,
+ fUseHostIOCache,
+ false /* fSetupMerge */,
+ false /* fBuiltinIOCache */,
+ 0 /* uMergeSource */,
+ 0 /* uMergeTarget */,
+ aMediumAtt,
+ pThis->mMachineState,
+ NULL /* phrc */,
+ true /* fAttachDetach */,
+ fForce /* fForceUnmount */,
+ false /* fHotplug */,
+ pUVM,
+ NULL /* paLedDevType */,
+ &pLunL0);
+ /* Dump the changed LUN if possible, dump the complete device otherwise */
+ CFGMR3Dump(pLunL0 ? pLunL0 : pCtlInst);
+
+ LogFlowFunc(("Returning %Rrc\n", rc));
+ return rc;
}
@@ -3913,11 +3780,12 @@ DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole,
* Attach a new storage device to the VM.
*
* @param aMediumAttachment The medium attachment which is added.
- * @param pVM Safe VM handle.
+ * @param pUVM Safe VM handle.
+ * @param fSilent Flag whether to notify the guest about the attached device.
*
* @note Locks this object for writing.
*/
-HRESULT Console::doStorageDeviceAttach(IMediumAttachment *aMediumAttachment, PVM pVM)
+HRESULT Console::doStorageDeviceAttach(IMediumAttachment *aMediumAttachment, PUVM pUVM, bool fSilent)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
@@ -3976,25 +3844,23 @@ HRESULT Console::doStorageDeviceAttach(IMediumAttachment *aMediumAttachment, PVM
AssertComRC(rc);
/*
+ * Suspend the VM first. The VM must not be running since it might have
+ * pending I/O to the drive which is being changed.
+ */
+ bool fResume = false;
+ rc = suspendBeforeConfigChange(pUVM, &alock, &fResume);
+ if (FAILED(rc))
+ return rc;
+
+ /*
* Call worker in EMT, that's faster and safer than doing everything
* using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
* here to make requests from under the lock in order to serialize them.
*/
PVMREQ pReq;
- int vrc = VMR3ReqCall(pVM,
- VMCPUID_ANY,
- &pReq,
- 0 /* no wait! */,
- VMREQFLAGS_VBOX_STATUS,
- (PFNRT)Console::attachStorageDevice,
- 7,
- this,
- pVM,
- pszDevice,
- uInstance,
- enmBus,
- fUseHostIOCache,
- aMediumAttachment);
+ int vrc = VMR3ReqCallU(pUVM, VMCPUID_ANY, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
+ (PFNRT)attachStorageDevice, 8,
+ this, pUVM, pszDevice, uInstance, enmBus, fUseHostIOCache, aMediumAttachment, fSilent);
/* release the lock before waiting for a result (EMT will call us back!) */
alock.release();
@@ -4008,6 +3874,9 @@ HRESULT Console::doStorageDeviceAttach(IMediumAttachment *aMediumAttachment, PVM
}
VMR3ReqFree(pReq);
+ if (fResume)
+ resumeAfterConfigChange(pUVM);
+
if (RT_SUCCESS(vrc))
{
LogFlowThisFunc(("Returns S_OK\n"));
@@ -4031,138 +3900,78 @@ HRESULT Console::doStorageDeviceAttach(IMediumAttachment *aMediumAttachment, PVM
* @returns VBox status code.
*
* @param pThis Pointer to the Console object.
- * @param pVM The VM handle.
+ * @param pUVM The VM handle.
* @param pcszDevice The PDM device name.
* @param uInstance The PDM device instance.
+ * @param fSilent Flag whether to inform the guest about the attached device.
*
* @thread EMT
+ * @note The VM must not be running since it might have pending I/O to the drive which is being changed.
*/
-DECLCALLBACK(int) Console::attachStorageDevice(Console *pConsole,
- PVM pVM,
+DECLCALLBACK(int) Console::attachStorageDevice(Console *pThis,
+ PUVM pUVM,
const char *pcszDevice,
unsigned uInstance,
StorageBus_T enmBus,
bool fUseHostIOCache,
- IMediumAttachment *aMediumAtt)
+ IMediumAttachment *aMediumAtt,
+ bool fSilent)
{
- LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p\n",
- pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt));
+ LogFlowFunc(("pThis=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p\n",
+ pThis, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt));
- AssertReturn(pConsole, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis, VERR_INVALID_PARAMETER);
- AutoCaller autoCaller(pConsole);
+ AutoCaller autoCaller(pThis);
AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
/*
- * Suspend the VM first.
- *
- * The VM must not be running since it might have pending I/O to
- * the drive which is being changed.
+ * Check the VM for correct state.
*/
- bool fResume;
- VMSTATE enmVMState = VMR3GetState(pVM);
- switch (enmVMState)
- {
- case VMSTATE_RESETTING:
- case VMSTATE_RUNNING:
- {
- LogFlowFunc(("Suspending the VM...\n"));
- /* disable the callback to prevent Console-level state change */
- pConsole->mVMStateChangeCallbackDisabled = true;
- int rc = VMR3Suspend(pVM);
- pConsole->mVMStateChangeCallbackDisabled = false;
- AssertRCReturn(rc, rc);
- fResume = true;
- break;
- }
-
- case VMSTATE_SUSPENDED:
- case VMSTATE_CREATED:
- case VMSTATE_OFF:
- fResume = false;
- break;
-
- case VMSTATE_RUNNING_LS:
- case VMSTATE_RUNNING_FT:
- return setErrorInternal(VBOX_E_INVALID_VM_STATE,
- COM_IIDOF(IConsole),
- getStaticComponentName(),
- (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
- false /*aWarning*/,
- true /*aLogIt*/);
-
- default:
- AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
- }
+ VMSTATE enmVMState = VMR3GetStateU(pUVM);
+ AssertReturn(enmVMState == VMSTATE_SUSPENDED, VERR_INVALID_STATE);
/* Determine the base path for the device instance. */
PCFGMNODE pCtlInst;
- pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
+ pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
- int rc = VINF_SUCCESS;
- int rcRet = VINF_SUCCESS;
-
- rcRet = pConsole->configMediumAttachment(pCtlInst,
- pcszDevice,
- uInstance,
- enmBus,
- fUseHostIOCache,
- false /* fSetupMerge */,
- false /* fBuiltinIOCache */,
- 0 /* uMergeSource */,
- 0 /* uMergeTarget */,
- aMediumAtt,
- pConsole->mMachineState,
- NULL /* phrc */,
- true /* fAttachDetach */,
- false /* fForceUnmount */,
- true /* fHotplug */,
- pVM,
- NULL /* paLedDevType */);
- /** @todo this dumps everything attached to this device instance, which
- * is more than necessary. Dumping the changed LUN would be enough. */
- CFGMR3Dump(pCtlInst);
-
- /*
- * Resume the VM if necessary.
- */
- if (fResume)
- {
- LogFlowFunc(("Resuming the VM...\n"));
- /* disable the callback to prevent Console-level state change */
- pConsole->mVMStateChangeCallbackDisabled = true;
- rc = VMR3Resume(pVM);
- pConsole->mVMStateChangeCallbackDisabled = false;
- AssertRC(rc);
- if (RT_FAILURE(rc))
- {
- /* too bad, we failed. try to sync the console state with the VMM state */
- vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
- }
- /** @todo: if we failed with drive mount, then the VMR3Resume
- * error (if any) will be hidden from the caller. For proper reporting
- * of such multiple errors to the caller we need to enhance the
- * IVirtualBoxError interface. For now, give the first error the higher
- * priority.
- */
- if (RT_SUCCESS(rcRet))
- rcRet = rc;
- }
-
- LogFlowFunc(("Returning %Rrc\n", rcRet));
- return rcRet;
+ PCFGMNODE pLunL0 = NULL;
+ int rc = pThis->configMediumAttachment(pCtlInst,
+ pcszDevice,
+ uInstance,
+ enmBus,
+ fUseHostIOCache,
+ false /* fSetupMerge */,
+ false /* fBuiltinIOCache */,
+ 0 /* uMergeSource */,
+ 0 /* uMergeTarget */,
+ aMediumAtt,
+ pThis->mMachineState,
+ NULL /* phrc */,
+ true /* fAttachDetach */,
+ false /* fForceUnmount */,
+ !fSilent /* fHotplug */,
+ pUVM,
+ NULL /* paLedDevType */,
+ &pLunL0);
+ /* Dump the changed LUN if possible, dump the complete device otherwise */
+ CFGMR3Dump(pLunL0 ? pLunL0 : pCtlInst);
+
+ LogFlowFunc(("Returning %Rrc\n", rc));
+ return rc;
}
/**
* Attach a new storage device to the VM.
*
* @param aMediumAttachment The medium attachment which is added.
- * @param pVM Safe VM handle.
+ * @param pUVM Safe VM handle.
+ * @param fSilent Flag whether to notify the guest about the detached device.
*
* @note Locks this object for writing.
*/
-HRESULT Console::doStorageDeviceDetach(IMediumAttachment *aMediumAttachment, PVM pVM)
+HRESULT Console::doStorageDeviceDetach(IMediumAttachment *aMediumAttachment, PUVM pUVM, bool fSilent)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
@@ -4218,24 +4027,23 @@ HRESULT Console::doStorageDeviceDetach(IMediumAttachment *aMediumAttachment, PVM
AssertComRC(rc);
/*
+ * Suspend the VM first. The VM must not be running since it might have
+ * pending I/O to the drive which is being changed.
+ */
+ bool fResume = false;
+ rc = suspendBeforeConfigChange(pUVM, &alock, &fResume);
+ if (FAILED(rc))
+ return rc;
+
+ /*
* Call worker in EMT, that's faster and safer than doing everything
* using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
* here to make requests from under the lock in order to serialize them.
*/
PVMREQ pReq;
- int vrc = VMR3ReqCall(pVM,
- VMCPUID_ANY,
- &pReq,
- 0 /* no wait! */,
- VMREQFLAGS_VBOX_STATUS,
- (PFNRT)Console::detachStorageDevice,
- 6,
- this,
- pVM,
- pszDevice,
- uInstance,
- enmBus,
- aMediumAttachment);
+ int vrc = VMR3ReqCallU(pUVM, VMCPUID_ANY, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
+ (PFNRT)detachStorageDevice, 7,
+ this, pUVM, pszDevice, uInstance, enmBus, aMediumAttachment, fSilent);
/* release the lock before waiting for a result (EMT will call us back!) */
alock.release();
@@ -4249,6 +4057,9 @@ HRESULT Console::doStorageDeviceDetach(IMediumAttachment *aMediumAttachment, PVM
}
VMR3ReqFree(pReq);
+ if (fResume)
+ resumeAfterConfigChange(pUVM);
+
if (RT_SUCCESS(vrc))
{
LogFlowThisFunc(("Returns S_OK\n"));
@@ -4271,72 +4082,39 @@ HRESULT Console::doStorageDeviceDetach(IMediumAttachment *aMediumAttachment, PVM
* @returns VBox status code.
*
* @param pThis Pointer to the Console object.
- * @param pVM The VM handle.
+ * @param pUVM The VM handle.
* @param pcszDevice The PDM device name.
* @param uInstance The PDM device instance.
+ * @param fSilent Flag whether to notify the guest about the detached device.
*
* @thread EMT
+ * @note The VM must not be running since it might have pending I/O to the drive which is being changed.
*/
-DECLCALLBACK(int) Console::detachStorageDevice(Console *pConsole,
- PVM pVM,
+DECLCALLBACK(int) Console::detachStorageDevice(Console *pThis,
+ PUVM pUVM,
const char *pcszDevice,
unsigned uInstance,
StorageBus_T enmBus,
- IMediumAttachment *pMediumAtt)
+ IMediumAttachment *pMediumAtt,
+ bool fSilent)
{
- LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, pMediumAtt=%p\n",
- pConsole, uInstance, pcszDevice, pcszDevice, enmBus, pMediumAtt));
+ LogFlowFunc(("pThis=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, pMediumAtt=%p\n",
+ pThis, uInstance, pcszDevice, pcszDevice, enmBus, pMediumAtt));
- AssertReturn(pConsole, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis, VERR_INVALID_PARAMETER);
- AutoCaller autoCaller(pConsole);
+ AutoCaller autoCaller(pThis);
AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
/*
- * Suspend the VM first.
- *
- * The VM must not be running since it might have pending I/O to
- * the drive which is being changed.
+ * Check the VM for correct state.
*/
- bool fResume;
- VMSTATE enmVMState = VMR3GetState(pVM);
- switch (enmVMState)
- {
- case VMSTATE_RESETTING:
- case VMSTATE_RUNNING:
- {
- LogFlowFunc(("Suspending the VM...\n"));
- /* disable the callback to prevent Console-level state change */
- pConsole->mVMStateChangeCallbackDisabled = true;
- int rc = VMR3Suspend(pVM);
- pConsole->mVMStateChangeCallbackDisabled = false;
- AssertRCReturn(rc, rc);
- fResume = true;
- break;
- }
-
- case VMSTATE_SUSPENDED:
- case VMSTATE_CREATED:
- case VMSTATE_OFF:
- fResume = false;
- break;
-
- case VMSTATE_RUNNING_LS:
- case VMSTATE_RUNNING_FT:
- return setErrorInternal(VBOX_E_INVALID_VM_STATE,
- COM_IIDOF(IConsole),
- getStaticComponentName(),
- (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
- false /*aWarning*/,
- true /*aLogIt*/);
-
- default:
- AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
- }
+ VMSTATE enmVMState = VMR3GetStateU(pUVM);
+ AssertReturn(enmVMState == VMSTATE_SUSPENDED, VERR_INVALID_STATE);
/* Determine the base path for the device instance. */
PCFGMNODE pCtlInst;
- pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
+ pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
#define H() AssertMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_GENERAL_FAILURE)
@@ -4362,14 +4140,19 @@ DECLCALLBACK(int) Console::detachStorageDevice(Console *pConsole,
pLunL0 = CFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN);
if (pLunL0)
{
- rc = PDMR3DeviceDetach(pVM, pcszDevice, uInstance, uLUN, 0);
+ uint32_t fFlags = 0;
+
+ if (fSilent)
+ fFlags |= PDM_TACH_FLAGS_NOT_HOT_PLUG;
+
+ rc = PDMR3DeviceDetach(pUVM, pcszDevice, uInstance, uLUN, fFlags);
if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
rc = VINF_SUCCESS;
AssertRCReturn(rc, rc);
CFGMR3RemoveNode(pLunL0);
Utf8Str devicePath = Utf8StrFmt("%s/%u/LUN#%u", pcszDevice, uInstance, uLUN);
- pConsole->mapMediumAttachments.erase(devicePath);
+ pThis->mapMediumAttachments.erase(devicePath);
}
else
@@ -4377,32 +4160,6 @@ DECLCALLBACK(int) Console::detachStorageDevice(Console *pConsole,
CFGMR3Dump(pCtlInst);
- /*
- * Resume the VM if necessary.
- */
- if (fResume)
- {
- LogFlowFunc(("Resuming the VM...\n"));
- /* disable the callback to prevent Console-level state change */
- pConsole->mVMStateChangeCallbackDisabled = true;
- rc = VMR3Resume(pVM);
- pConsole->mVMStateChangeCallbackDisabled = false;
- AssertRC(rc);
- if (RT_FAILURE(rc))
- {
- /* too bad, we failed. try to sync the console state with the VMM state */
- vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
- }
- /** @todo: if we failed with drive mount, then the VMR3Resume
- * error (if any) will be hidden from the caller. For proper reporting
- * of such multiple errors to the caller we need to enhance the
- * IVirtualBoxError interface. For now, give the first error the higher
- * priority.
- */
- if (RT_SUCCESS(rcRet))
- rcRet = rc;
- }
-
LogFlowFunc(("Returning %Rrc\n", rcRet));
return rcRet;
}
@@ -4423,7 +4180,7 @@ HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL c
HRESULT rc = S_OK;
- /* don't trigger network change if the VM isn't running */
+ /* don't trigger network changes if the VM isn't running */
SafeVMPtrQuiet ptrVM(this);
if (ptrVM.isOk())
{
@@ -4456,7 +4213,7 @@ HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL c
alock.release();
PPDMIBASE pBase;
- int vrc = PDMR3QueryDeviceLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
+ int vrc = PDMR3QueryDeviceLun(ptrVM.rawUVM(), pszAdapterName, ulInstance, 0, &pBase);
if (RT_SUCCESS(vrc))
{
Assert(pBase);
@@ -4473,7 +4230,7 @@ HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL c
}
if (RT_SUCCESS(vrc) && changeAdapter)
{
- VMSTATE enmVMState = VMR3GetState(ptrVM);
+ VMSTATE enmVMState = VMR3GetStateU(ptrVM.rawUVM());
if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */
|| enmVMState == VMSTATE_SUSPENDED)
{
@@ -4483,7 +4240,7 @@ HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL c
ComAssertRC(vrc);
}
- rc = doNetworkAdapterChange(ptrVM, pszAdapterName, ulInstance, 0, aNetworkAdapter);
+ rc = doNetworkAdapterChange(ptrVM.rawUVM(), pszAdapterName, ulInstance, 0, aNetworkAdapter);
if (fTraceEnabled && fCableConnected && pINetCfg)
{
@@ -4536,7 +4293,7 @@ HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove,
HRESULT rc = S_OK;
- /* don't trigger nat engine change if the VM isn't running */
+ /* don't trigger NAT engine changes if the VM isn't running */
SafeVMPtrQuiet ptrVM(this);
if (ptrVM.isOk())
{
@@ -4563,7 +4320,7 @@ HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove,
const char *pszAdapterName = networkAdapterTypeToName(adapterType);
PPDMIBASE pBase;
- int vrc = PDMR3QueryLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
+ int vrc = PDMR3QueryLun(ptrVM.rawUVM(), pszAdapterName, ulInstance, 0, &pBase);
if (RT_FAILURE(vrc))
{
ComAssertRC(vrc);
@@ -4596,8 +4353,8 @@ HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove,
bool fUdp = aProto == NATProtocol_UDP;
vrc = pNetNatCfg->pfnRedirectRuleCommand(pNetNatCfg, !!aNatRuleRemove, fUdp,
- Utf8Str(aHostIP).c_str(), aHostPort, Utf8Str(aGuestIP).c_str(),
- aGuestPort);
+ Utf8Str(aHostIP).c_str(), (uint16_t)aHostPort, Utf8Str(aGuestIP).c_str(),
+ (uint16_t)aGuestPort);
if (RT_FAILURE(vrc))
rc = E_FAIL;
} while (0); /* break loop */
@@ -4608,19 +4365,28 @@ HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove,
return rc;
}
+VMMDevMouseInterface *Console::getVMMDevMouseInterface()
+{
+ return m_pVMMDev;
+}
+
+DisplayMouseInterface *Console::getDisplayMouseInterface()
+{
+ return mDisplay;
+}
/**
* Process a network adaptor change.
*
* @returns COM status code.
*
- * @parma pVM The VM handle (caller hold this safely).
+ * @parma pUVM The VM handle (caller hold this safely).
* @param pszDevice The PDM device name.
* @param uInstance The PDM device instance.
* @param uLun The PDM LUN number of the drive.
* @param aNetworkAdapter The network adapter whose attachment needs to be changed
*/
-HRESULT Console::doNetworkAdapterChange(PVM pVM,
+HRESULT Console::doNetworkAdapterChange(PUVM pUVM,
const char *pszDevice,
unsigned uInstance,
unsigned uLun,
@@ -4632,10 +4398,13 @@ HRESULT Console::doNetworkAdapterChange(PVM pVM,
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
- /* Get the VM handle. */
- SafeVMPtr ptrVM(this);
- if (!ptrVM.isOk())
- return ptrVM.rc();
+ /*
+ * Suspend the VM first.
+ */
+ bool fResume = false;
+ int rc = suspendBeforeConfigChange(pUVM, NULL, &fResume);
+ if (FAILED(rc))
+ return rc;
/*
* Call worker in EMT, that's faster and safer than doing everything
@@ -4643,9 +4412,9 @@ HRESULT Console::doNetworkAdapterChange(PVM pVM,
* here to make requests from under the lock in order to serialize them.
*/
PVMREQ pReq;
- int vrc = VMR3ReqCall(pVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
- (PFNRT) Console::changeNetworkAttachment, 6,
- this, ptrVM.raw(), pszDevice, uInstance, uLun, aNetworkAdapter);
+ int vrc = VMR3ReqCallU(pUVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
+ (PFNRT)changeNetworkAttachment, 6,
+ this, pUVM, pszDevice, uInstance, uLun, aNetworkAdapter);
if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
{
@@ -4656,6 +4425,9 @@ HRESULT Console::doNetworkAdapterChange(PVM pVM,
}
VMR3ReqFree(pReq);
+ if (fResume)
+ resumeAfterConfigChange(pUVM);
+
if (RT_SUCCESS(vrc))
{
LogFlowThisFunc(("Returns S_OK\n"));
@@ -4674,7 +4446,7 @@ HRESULT Console::doNetworkAdapterChange(PVM pVM,
* @returns VBox status code.
*
* @param pThis Pointer to the Console object.
- * @param pVM The VM handle.
+ * @param pUVM The VM handle.
* @param pszDevice The PDM device name.
* @param uInstance The PDM device instance.
* @param uLun The PDM LUN number of the drive.
@@ -4682,9 +4454,10 @@ HRESULT Console::doNetworkAdapterChange(PVM pVM,
*
* @thread EMT
* @note Locks the Console object for writing.
+ * @note The VM must not be running.
*/
DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
- PVM pVM,
+ PUVM pUVM,
const char *pszDevice,
unsigned uInstance,
unsigned uLun,
@@ -4717,76 +4490,21 @@ DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
/*
- * Suspend the VM first.
- *
- * The VM must not be running since it might have pending I/O to
- * the drive which is being changed.
+ * Check the VM for correct state.
*/
- bool fResume;
- VMSTATE enmVMState = VMR3GetState(pVM);
- switch (enmVMState)
- {
- case VMSTATE_RESETTING:
- case VMSTATE_RUNNING:
- {
- LogFlowFunc(("Suspending the VM...\n"));
- /* disable the callback to prevent Console-level state change */
- pThis->mVMStateChangeCallbackDisabled = true;
- int rc = VMR3Suspend(pVM);
- pThis->mVMStateChangeCallbackDisabled = false;
- AssertRCReturn(rc, rc);
- fResume = true;
- break;
- }
-
- case VMSTATE_SUSPENDED:
- case VMSTATE_CREATED:
- case VMSTATE_OFF:
- fResume = false;
- break;
-
- default:
- AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
- }
-
- int rc = VINF_SUCCESS;
- int rcRet = VINF_SUCCESS;
+ VMSTATE enmVMState = VMR3GetStateU(pUVM);
+ AssertReturn(enmVMState == VMSTATE_SUSPENDED, VERR_INVALID_STATE);
PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
- PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%d/", pszDevice, uInstance);
+ PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%d/", pszDevice, uInstance);
AssertRelease(pInst);
- rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst,
- true /*fAttachDetach*/, false /*fIgnoreConnectFailure*/);
-
- /*
- * Resume the VM if necessary.
- */
- if (fResume)
- {
- LogFlowFunc(("Resuming the VM...\n"));
- /* disable the callback to prevent Console-level state change */
- pThis->mVMStateChangeCallbackDisabled = true;
- rc = VMR3Resume(pVM);
- pThis->mVMStateChangeCallbackDisabled = false;
- AssertRC(rc);
- if (RT_FAILURE(rc))
- {
- /* too bad, we failed. try to sync the console state with the VMM state */
- vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
- }
- /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
- // error (if any) will be hidden from the caller. For proper reporting
- // of such multiple errors to the caller we need to enhance the
- // IVirtualBoxError interface. For now, give the first error the higher
- // priority.
- if (RT_SUCCESS(rcRet))
- rcRet = rc;
- }
+ int rc = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst,
+ true /*fAttachDetach*/, false /*fIgnoreConnectFailure*/);
- LogFlowFunc(("Returning %Rrc\n", rcRet));
- return rcRet;
+ LogFlowFunc(("Returning %Rrc\n", rc));
+ return rc;
}
@@ -4850,11 +4568,11 @@ HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForc
HRESULT rc = S_OK;
- /* don't trigger medium change if the VM isn't running */
+ /* don't trigger medium changes if the VM isn't running */
SafeVMPtrQuiet ptrVM(this);
if (ptrVM.isOk())
{
- rc = doMediumChange(aMediumAttachment, !!aForce, ptrVM);
+ rc = doMediumChange(aMediumAttachment, !!aForce, ptrVM.rawUVM());
ptrVM.release();
}
@@ -4880,14 +4598,14 @@ HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
HRESULT rc = S_OK;
- /* don't trigger CPU change if the VM isn't running */
+ /* don't trigger CPU changes if the VM isn't running */
SafeVMPtrQuiet ptrVM(this);
if (ptrVM.isOk())
{
if (aRemove)
- rc = doCPURemove(aCPU, ptrVM);
+ rc = doCPURemove(aCPU, ptrVM.rawUVM());
else
- rc = doCPUAdd(aCPU, ptrVM);
+ rc = doCPUAdd(aCPU, ptrVM.rawUVM());
ptrVM.release();
}
@@ -4925,7 +4643,7 @@ HRESULT Console::onCPUExecutionCapChange(ULONG aExecutionCap)
)
{
/* No need to call in the EMT thread. */
- rc = VMR3SetCpuExecutionCap(ptrVM, aExecutionCap);
+ rc = VMR3SetCpuExecutionCap(ptrVM.rawUVM(), aExecutionCap);
}
else
rc = setInvalidMachineStateError();
@@ -4959,7 +4677,7 @@ HRESULT Console::onClipboardModeChange(ClipboardMode_T aClipboardMode)
HRESULT rc = S_OK;
- /* don't trigger the Clipboard mode change if the VM isn't running */
+ /* don't trigger the clipboard mode change if the VM isn't running */
SafeVMPtrQuiet ptrVM(this);
if (ptrVM.isOk())
{
@@ -4999,7 +4717,7 @@ HRESULT Console::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
HRESULT rc = S_OK;
- /* don't trigger the Drag'n'drop mode change if the VM isn't running */
+ /* don't trigger the drag'n'drop mode change if the VM isn't running */
SafeVMPtrQuiet ptrVM(this);
if (ptrVM.isOk())
{
@@ -5037,42 +4755,63 @@ HRESULT Console::onVRDEServerChange(BOOL aRestart)
HRESULT rc = S_OK;
- if ( mVRDEServer
- && ( mMachineState == MachineState_Running
- || mMachineState == MachineState_Teleporting
- || mMachineState == MachineState_LiveSnapshotting
- )
- )
+ /* don't trigger VRDE server changes if the VM isn't running */
+ SafeVMPtrQuiet ptrVM(this);
+ if (ptrVM.isOk())
{
- BOOL vrdpEnabled = FALSE;
+ /* Serialize. */
+ if (mfVRDEChangeInProcess)
+ mfVRDEChangePending = true;
+ else
+ {
+ do {
+ mfVRDEChangeInProcess = true;
+ mfVRDEChangePending = false;
+
+ if ( mVRDEServer
+ && ( mMachineState == MachineState_Running
+ || mMachineState == MachineState_Teleporting
+ || mMachineState == MachineState_LiveSnapshotting
+ || mMachineState == MachineState_Paused
+ )
+ )
+ {
+ BOOL vrdpEnabled = FALSE;
- rc = mVRDEServer->COMGETTER(Enabled)(&vrdpEnabled);
- ComAssertComRCRetRC(rc);
+ rc = mVRDEServer->COMGETTER(Enabled)(&vrdpEnabled);
+ ComAssertComRCRetRC(rc);
- if (aRestart)
- {
- /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
- alock.release();
+ if (aRestart)
+ {
+ /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
+ alock.release();
- if (vrdpEnabled)
- {
- // If there was no VRDP server started the 'stop' will do nothing.
- // However if a server was started and this notification was called,
- // we have to restart the server.
- mConsoleVRDPServer->Stop();
+ if (vrdpEnabled)
+ {
+ // If there was no VRDP server started the 'stop' will do nothing.
+ // However if a server was started and this notification was called,
+ // we have to restart the server.
+ mConsoleVRDPServer->Stop();
+
+ if (RT_FAILURE(mConsoleVRDPServer->Launch()))
+ rc = E_FAIL;
+ else
+ mConsoleVRDPServer->EnableConnections();
+ }
+ else
+ mConsoleVRDPServer->Stop();
- if (RT_FAILURE(mConsoleVRDPServer->Launch()))
- rc = E_FAIL;
+ alock.acquire();
+ }
+ }
else
- mConsoleVRDPServer->EnableConnections();
- }
- else
- {
- mConsoleVRDPServer->Stop();
- }
+ rc = setInvalidMachineStateError();
- alock.acquire();
+ mfVRDEChangeInProcess = false;
+ } while (mfVRDEChangePending && SUCCEEDED(rc));
}
+
+ ptrVM.release();
}
/* notify console callbacks on success */
@@ -5093,6 +4832,55 @@ void Console::onVRDEServerInfoChange()
fireVRDEServerInfoChangedEvent(mEventSource);
}
+HRESULT Console::onVideoCaptureChange()
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturnRC(autoCaller.rc());
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT rc = S_OK;
+
+ /* don't trigger video capture changes if the VM isn't running */
+ SafeVMPtrQuiet ptrVM(this);
+ if (ptrVM.isOk())
+ {
+ BOOL fEnabled;
+ rc = mMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);
+ SafeArray<BOOL> screens;
+ if (SUCCEEDED(rc))
+ rc = mMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens));
+ if (mDisplay)
+ {
+ int vrc = VINF_SUCCESS;
+ if (SUCCEEDED(rc))
+ vrc = mDisplay->VideoCaptureEnableScreens(ComSafeArrayAsInParam(screens));
+ if (RT_SUCCESS(vrc))
+ {
+ if (fEnabled)
+ {
+ vrc = mDisplay->VideoCaptureStart();
+ if (RT_FAILURE(vrc))
+ rc = setError(E_FAIL, tr("Unable to start video capturing (%Rrc)"), vrc);
+ }
+ else
+ mDisplay->VideoCaptureStop();
+ }
+ else
+ rc = setError(E_FAIL, tr("Unable to set screens for capturing (%Rrc)"), vrc);
+ }
+ ptrVM.release();
+ }
+
+ /* notify console callbacks on success */
+ if (SUCCEEDED(rc))
+ {
+ alock.release();
+ fireVideoCaptureChangedEvent(mEventSource);
+ }
+
+ return rc;
+}
/**
* Called by IInternalSessionControl::OnUSBControllerChange().
@@ -5181,7 +4969,7 @@ HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aE
}
/* Don't proceed unless there's at least one USB hub. */
- if (!PDMR3USBHasHub(ptrVM))
+ if (!PDMR3UsbHasHub(ptrVM.rawUVM()))
{
LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
return E_FAIL;
@@ -5318,7 +5106,7 @@ HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
HRESULT rc = S_OK;
- /* don't trigger the CPU priority change if the VM isn't running */
+ /* don't trigger bandwidth group changes if the VM isn't running */
SafeVMPtrQuiet ptrVM(this);
if (ptrVM.isOk())
{
@@ -5341,12 +5129,10 @@ HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
{
int vrc = VINF_SUCCESS;
if (enmType == BandwidthGroupType_Disk)
- vrc = PDMR3AsyncCompletionBwMgrSetMaxForFile(ptrVM, Utf8Str(strName).c_str(),
- cMax);
+ vrc = PDMR3AsyncCompletionBwMgrSetMaxForFile(ptrVM.rawUVM(), Utf8Str(strName).c_str(), (uint32_t)cMax);
#ifdef VBOX_WITH_NETSHAPER
else if (enmType == BandwidthGroupType_Network)
- vrc = PDMR3NsBwGroupSetLimit(ptrVM, Utf8Str(strName).c_str(),
- cMax);
+ vrc = PDMR3NsBwGroupSetLimit(ptrVM.rawUVM(), Utf8Str(strName).c_str(), cMax);
else
rc = E_NOTIMPL;
#endif /* VBOX_WITH_NETSHAPER */
@@ -5374,7 +5160,7 @@ HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
*
* @note Locks this object for writing.
*/
-HRESULT Console::onStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove)
+HRESULT Console::onStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove, BOOL aSilent)
{
LogFlowThisFunc(("\n"));
@@ -5383,25 +5169,67 @@ HRESULT Console::onStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOO
HRESULT rc = S_OK;
- /* don't trigger medium change if the VM isn't running */
+ /* don't trigger medium changes if the VM isn't running */
SafeVMPtrQuiet ptrVM(this);
if (ptrVM.isOk())
{
if (aRemove)
- rc = doStorageDeviceDetach(aMediumAttachment, ptrVM);
+ rc = doStorageDeviceDetach(aMediumAttachment, ptrVM.rawUVM(), RT_BOOL(aSilent));
else
- rc = doStorageDeviceAttach(aMediumAttachment, ptrVM);
+ rc = doStorageDeviceAttach(aMediumAttachment, ptrVM.rawUVM(), RT_BOOL(aSilent));
ptrVM.release();
}
/* notify console callbacks on success */
if (SUCCEEDED(rc))
- fireStorageDeviceChangedEvent(mEventSource, aMediumAttachment, aRemove);
+ fireStorageDeviceChangedEvent(mEventSource, aMediumAttachment, aRemove, aSilent);
LogFlowThisFunc(("Leaving rc=%#x\n", rc));
return rc;
}
+HRESULT Console::onExtraDataChange(IN_BSTR aMachineId, IN_BSTR aKey, IN_BSTR aVal)
+{
+ LogFlowThisFunc(("\n"));
+
+ AutoCaller autoCaller(this);
+ AssertComRCReturnRC(autoCaller.rc());
+
+ if (!aMachineId)
+ return S_OK;
+
+ HRESULT hrc = S_OK;
+ Bstr idMachine(aMachineId);
+ Bstr idSelf;
+ hrc = mMachine->COMGETTER(Id)(idSelf.asOutParam());
+ if ( FAILED(hrc)
+ || idMachine != idSelf)
+ return hrc;
+
+ /* don't do anything if the VM isn't running */
+ SafeVMPtrQuiet ptrVM(this);
+ if (ptrVM.isOk())
+ {
+ Bstr strKey(aKey);
+ Bstr strVal(aVal);
+
+ if (strKey == "VBoxInternal2/TurnResetIntoPowerOff")
+ {
+ int vrc = VMR3SetPowerOffInsteadOfReset(ptrVM.rawUVM(), strVal == "1");
+ AssertRC(vrc);
+ }
+
+ ptrVM.release();
+ }
+
+ /* notify console callbacks on success */
+ if (SUCCEEDED(hrc))
+ fireExtraDataChangedEvent(mEventSource, aMachineId, aKey, aVal);
+
+ LogFlowThisFunc(("Leaving hrc=%#x\n", hrc));
+ return hrc;
+}
+
/**
* @note Temporarily locks this object for writing.
*/
@@ -5423,12 +5251,13 @@ HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
- /* protect mpVM (if not NULL) */
- AutoVMCallerWeak autoVMCaller(this);
- if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
+ /* protect mpUVM (if not NULL) */
+ SafeVMPtrQuiet ptrVM(this);
+ if (FAILED(ptrVM.rc()))
+ return ptrVM.rc();
/* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
- * autoVMCaller, so there is no need to hold a lock of this */
+ * ptrVM, so there is no need to hold a lock of this */
HRESULT rc = E_UNEXPECTED;
using namespace guestProp;
@@ -5492,51 +5321,56 @@ HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
#ifndef VBOX_WITH_GUEST_PROPS
ReturnComNotImplemented();
#else /* VBOX_WITH_GUEST_PROPS */
- if (!VALID_PTR(aName))
- return E_INVALIDARG;
- if ((aValue != NULL) && !VALID_PTR(aValue))
- return E_INVALIDARG;
- if ((aFlags != NULL) && !VALID_PTR(aFlags))
- return E_INVALIDARG;
+ if (!RT_VALID_PTR(aName))
+ return setError(E_INVALIDARG, tr("Name cannot be NULL or an invalid pointer"));
+ if (aValue != NULL && !RT_VALID_PTR(aValue))
+ return setError(E_INVALIDARG, tr("Invalid value pointer"));
+ if (aFlags != NULL && !RT_VALID_PTR(aFlags))
+ return setError(E_INVALIDARG, tr("Invalid flags pointer"));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
- /* protect mpVM (if not NULL) */
- AutoVMCallerWeak autoVMCaller(this);
- if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
+ /* protect mpUVM (if not NULL) */
+ SafeVMPtrQuiet ptrVM(this);
+ if (FAILED(ptrVM.rc()))
+ return ptrVM.rc();
/* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
- * autoVMCaller, so there is no need to hold a lock of this */
+ * ptrVM, so there is no need to hold a lock of this */
- HRESULT rc = E_UNEXPECTED;
using namespace guestProp;
VBOXHGCMSVCPARM parm[3];
- Utf8Str Utf8Name = aName;
- int vrc = VINF_SUCCESS;
+ Utf8Str Utf8Name = aName;
parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
/* The + 1 is the null terminator */
parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
- Utf8Str Utf8Value = aValue;
+
+ Utf8Str Utf8Value;
if (aValue != NULL)
{
+ Utf8Value = aValue;
parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
- parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
+ parm[1].u.pointer.addr = (void *)Utf8Value.c_str();
/* The + 1 is the null terminator */
parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
}
- Utf8Str Utf8Flags = aFlags;
+
+ Utf8Str Utf8Flags;
if (aFlags != NULL)
{
+ Utf8Flags = aFlags;
parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
/* The + 1 is the null terminator */
parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
}
- if ((aValue != NULL) && (aFlags != NULL))
+
+ int vrc;
+ if (aValue != NULL && aFlags != NULL)
vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
3, &parm[0]);
else if (aValue != NULL)
@@ -5545,13 +5379,12 @@ HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
else
vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
1, &parm[0]);
+ HRESULT hrc;
if (RT_SUCCESS(vrc))
- rc = S_OK;
+ hrc = S_OK;
else
- rc = setError(E_UNEXPECTED,
- tr("The service call failed with the error %Rrc"),
- vrc);
- return rc;
+ hrc = setError(E_UNEXPECTED, tr("The service call failed with the error %Rrc"), vrc);
+ return hrc;
#endif /* VBOX_WITH_GUEST_PROPS */
}
@@ -5582,9 +5415,10 @@ HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
- /* protect mpVM (if not NULL) */
+ /* protect mpUVM (if not NULL) */
AutoVMCallerWeak autoVMCaller(this);
- if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
+ if (FAILED(autoVMCaller.rc()))
+ return autoVMCaller.rc();
/* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
* autoVMCaller, so there is no need to hold a lock of this */
@@ -5614,10 +5448,6 @@ static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
*/
HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
ULONG aSourceIdx, ULONG aTargetIdx,
- IMedium *aSource, IMedium *aTarget,
- BOOL aMergeForward,
- IMedium *aParentForTarget,
- ComSafeArrayIn(IMedium *, aChildrenToReparent),
IProgress *aProgress)
{
AutoCaller autoCaller(this);
@@ -5711,34 +5541,22 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
alock.release();
/* Pause the VM, as it might have pending IO on this drive */
- VMSTATE enmVMState = VMR3GetState(ptrVM);
+ VMSTATE enmVMState = VMR3GetStateU(ptrVM.rawUVM());
if (mMachineState == MachineState_DeletingSnapshotOnline)
{
LogFlowFunc(("Suspending the VM...\n"));
/* disable the callback to prevent Console-level state change */
mVMStateChangeCallbackDisabled = true;
- int vrc2 = VMR3Suspend(ptrVM);
+ int vrc2 = VMR3Suspend(ptrVM.rawUVM(), VMSUSPENDREASON_RECONFIG);
mVMStateChangeCallbackDisabled = false;
AssertRCReturn(vrc2, E_FAIL);
}
- vrc = VMR3ReqCallWait(ptrVM,
- VMCPUID_ANY,
- (PFNRT)reconfigureMediumAttachment,
- 13,
- this,
- ptrVM.raw(),
- pcszDevice,
- uInstance,
- enmBus,
- fUseHostIOCache,
- fBuiltinIOCache,
- true /* fSetupMerge */,
- aSourceIdx,
- aTargetIdx,
- aMediumAttachment,
- mMachineState,
- &rc);
+ vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY,
+ (PFNRT)reconfigureMediumAttachment, 13,
+ this, ptrVM.rawUVM(), pcszDevice, uInstance, enmBus, fUseHostIOCache,
+ fBuiltinIOCache, true /* fSetupMerge */, aSourceIdx, aTargetIdx,
+ aMediumAttachment, mMachineState, &rc);
/* error handling is after resuming the VM */
if (mMachineState == MachineState_DeletingSnapshotOnline)
@@ -5746,13 +5564,13 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
LogFlowFunc(("Resuming the VM...\n"));
/* disable the callback to prevent Console-level state change */
mVMStateChangeCallbackDisabled = true;
- int vrc2 = VMR3Resume(ptrVM);
+ int vrc2 = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_RECONFIG);
mVMStateChangeCallbackDisabled = false;
if (RT_FAILURE(vrc2))
{
/* too bad, we failed. try to sync the console state with the VMM state */
AssertLogRelRC(vrc2);
- vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
+ vmstateChangeCallback(ptrVM.rawUVM(), VMSTATE_SUSPENDED, enmVMState, this);
}
}
@@ -5763,7 +5581,7 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
PPDMIBASE pIBase = NULL;
PPDMIMEDIA pIMedium = NULL;
- vrc = PDMR3QueryDriverOnLun(ptrVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
+ vrc = PDMR3QueryDriverOnLun(ptrVM.rawUVM(), pcszDevice, uInstance, uLUN, "VD", &pIBase);
if (RT_SUCCESS(vrc))
{
if (pIBase)
@@ -5782,39 +5600,25 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
/* Pause the VM, as it might have pending IO on this drive */
- enmVMState = VMR3GetState(ptrVM);
+ enmVMState = VMR3GetStateU(ptrVM.rawUVM());
if (mMachineState == MachineState_DeletingSnapshotOnline)
{
LogFlowFunc(("Suspending the VM...\n"));
/* disable the callback to prevent Console-level state change */
mVMStateChangeCallbackDisabled = true;
- int vrc2 = VMR3Suspend(ptrVM);
+ int vrc2 = VMR3Suspend(ptrVM.rawUVM(), VMSUSPENDREASON_RECONFIG);
mVMStateChangeCallbackDisabled = false;
AssertRCReturn(vrc2, E_FAIL);
}
/* Update medium chain and state now, so that the VM can continue. */
- rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
- aMergeForward, aParentForTarget,
- ComSafeArrayInArg(aChildrenToReparent));
-
- vrc = VMR3ReqCallWait(ptrVM,
- VMCPUID_ANY,
- (PFNRT)reconfigureMediumAttachment,
- 13,
- this,
- ptrVM.raw(),
- pcszDevice,
- uInstance,
- enmBus,
- fUseHostIOCache,
- fBuiltinIOCache,
- false /* fSetupMerge */,
- 0 /* uMergeSource */,
- 0 /* uMergeTarget */,
- aMediumAttachment,
- mMachineState,
- &rc);
+ rc = mControl->FinishOnlineMergeMedium();
+
+ vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY,
+ (PFNRT)reconfigureMediumAttachment, 13,
+ this, ptrVM.rawUVM(), pcszDevice, uInstance, enmBus, fUseHostIOCache,
+ fBuiltinIOCache, false /* fSetupMerge */, 0 /* uMergeSource */,
+ 0 /* uMergeTarget */, aMediumAttachment, mMachineState, &rc);
/* error handling is after resuming the VM */
if (mMachineState == MachineState_DeletingSnapshotOnline)
@@ -5822,13 +5626,13 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
LogFlowFunc(("Resuming the VM...\n"));
/* disable the callback to prevent Console-level state change */
mVMStateChangeCallbackDisabled = true;
- int vrc2 = VMR3Resume(ptrVM);
+ int vrc2 = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_RECONFIG);
mVMStateChangeCallbackDisabled = false;
AssertRC(vrc2);
if (RT_FAILURE(vrc2))
{
/* too bad, we failed. try to sync the console state with the VMM state */
- vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
+ vmstateChangeCallback(ptrVM.rawUVM(), VMSTATE_SUSPENDED, enmVMState, this);
}
}
@@ -5842,6 +5646,23 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
/**
+ * Load an HGCM service.
+ *
+ * Main purpose of this method is to allow extension packs to load HGCM
+ * service modules, which they can't, because the HGCM functionality lives
+ * in module VBoxC (and ConsoleImpl.cpp is part of it and thus can call it).
+ * Extension modules must not link directly against VBoxC, (XP)COM is
+ * handling this.
+ */
+int Console::hgcmLoadService(const char *pszServiceLibrary, const char *pszServiceName)
+{
+ /* Everyone seems to delegate all HGCM calls to VMMDev, so stick to this
+ * convention. Adds one level of indirection for no obvious reason. */
+ AssertPtrReturn(m_pVMMDev, VERR_INVALID_STATE);
+ return m_pVMMDev->hgcmLoadService(pszServiceLibrary, pszServiceName);
+}
+
+/**
* Merely passes the call to Guest::enableVMMStatistics().
*/
void Console::enableVMMStatistics(BOOL aEnable)
@@ -5851,6 +5672,289 @@ void Console::enableVMMStatistics(BOOL aEnable)
}
/**
+ * Worker for Console::Pause and internal entry point for pausing a VM for
+ * a specific reason.
+ */
+HRESULT Console::pause(Reason_T aReason)
+{
+ LogFlowThisFuncEnter();
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ switch (mMachineState)
+ {
+ case MachineState_Running:
+ case MachineState_Teleporting:
+ case MachineState_LiveSnapshotting:
+ break;
+
+ case MachineState_Paused:
+ case MachineState_TeleportingPausedVM:
+ case MachineState_Saving:
+ return setError(VBOX_E_INVALID_VM_STATE, tr("Already paused"));
+
+ default:
+ return setInvalidMachineStateError();
+ }
+
+ /* get the VM handle. */
+ SafeVMPtr ptrVM(this);
+ if (!ptrVM.isOk())
+ return ptrVM.rc();
+
+ /* release the lock before a VMR3* call (EMT will call us back)! */
+ alock.release();
+
+ LogFlowThisFunc(("Sending PAUSE request...\n"));
+ if (aReason != Reason_Unspecified)
+ LogRel(("Pausing VM execution, reason \"%s\"\n", Global::stringifyReason(aReason)));
+
+ /** @todo r=klaus make use of aReason */
+ VMSUSPENDREASON enmReason = VMSUSPENDREASON_USER;
+ if (aReason == Reason_HostSuspend)
+ enmReason = VMSUSPENDREASON_HOST_SUSPEND;
+ else if (aReason == Reason_HostBatteryLow)
+ enmReason = VMSUSPENDREASON_HOST_BATTERY_LOW;
+ int vrc = VMR3Suspend(ptrVM.rawUVM(), enmReason);
+
+ HRESULT hrc = S_OK;
+ if (RT_FAILURE(vrc))
+ hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
+
+ LogFlowThisFunc(("hrc=%Rhrc\n", hrc));
+ LogFlowThisFuncLeave();
+ return hrc;
+}
+
+/**
+ * Worker for Console::Resume and internal entry point for resuming a VM for
+ * a specific reason.
+ */
+HRESULT Console::resume(Reason_T aReason)
+{
+ LogFlowThisFuncEnter();
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (mMachineState != MachineState_Paused)
+ return setError(VBOX_E_INVALID_VM_STATE,
+ tr("Cannot resume the machine as it is not paused (machine state: %s)"),
+ Global::stringifyMachineState(mMachineState));
+
+ /* get the VM handle. */
+ SafeVMPtr ptrVM(this);
+ if (!ptrVM.isOk())
+ return ptrVM.rc();
+
+ /* release the lock before a VMR3* call (EMT will call us back)! */
+ alock.release();
+
+ LogFlowThisFunc(("Sending RESUME request...\n"));
+ if (aReason != Reason_Unspecified)
+ LogRel(("Resuming VM execution, reason \"%s\"\n", Global::stringifyReason(aReason)));
+
+ int vrc;
+ if (VMR3GetStateU(ptrVM.rawUVM()) == VMSTATE_CREATED)
+ {
+#ifdef VBOX_WITH_EXTPACK
+ vrc = mptrExtPackManager->callAllVmPowerOnHooks(this, VMR3GetVM(ptrVM.rawUVM()));
+#else
+ vrc = VINF_SUCCESS;
+#endif
+ if (RT_SUCCESS(vrc))
+ vrc = VMR3PowerOn(ptrVM.rawUVM()); /* (PowerUpPaused) */
+ }
+ else
+ {
+ VMRESUMEREASON enmReason = VMRESUMEREASON_USER;
+ if (aReason == Reason_HostResume)
+ enmReason = VMRESUMEREASON_HOST_RESUME;
+ vrc = VMR3Resume(ptrVM.rawUVM(), enmReason);
+ }
+
+ HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
+ setError(VBOX_E_VM_ERROR,
+ tr("Could not resume the machine execution (%Rrc)"),
+ vrc);
+
+ LogFlowThisFunc(("rc=%Rhrc\n", rc));
+ LogFlowThisFuncLeave();
+ return rc;
+}
+
+/**
+ * Worker for Console::SaveState and internal entry point for saving state of
+ * a VM for a specific reason.
+ */
+HRESULT Console::saveState(Reason_T aReason, IProgress **aProgress)
+{
+ LogFlowThisFuncEnter();
+
+ CheckComArgOutPointerValid(aProgress);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
+ if ( mMachineState != MachineState_Running
+ && mMachineState != MachineState_Paused)
+ {
+ return setError(VBOX_E_INVALID_VM_STATE,
+ tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
+ Global::stringifyMachineState(mMachineState));
+ }
+
+ if (aReason != Reason_Unspecified)
+ LogRel(("Saving state of VM, reason \"%s\"\n", Global::stringifyReason(aReason)));
+
+ /* memorize the current machine state */
+ MachineState_T lastMachineState = mMachineState;
+
+ if (mMachineState == MachineState_Running)
+ {
+ /* get the VM handle. */
+ SafeVMPtr ptrVM(this);
+ if (!ptrVM.isOk())
+ return ptrVM.rc();
+
+ /* release the lock before a VMR3* call (EMT will call us back)! */
+ alock.release();
+ VMSUSPENDREASON enmReason = VMSUSPENDREASON_USER;
+ if (aReason == Reason_HostSuspend)
+ enmReason = VMSUSPENDREASON_HOST_SUSPEND;
+ else if (aReason == Reason_HostBatteryLow)
+ enmReason = VMSUSPENDREASON_HOST_BATTERY_LOW;
+ int vrc = VMR3Suspend(ptrVM.rawUVM(), enmReason);
+ alock.acquire();
+
+ HRESULT hrc = S_OK;
+ if (RT_FAILURE(vrc))
+ hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
+ if (FAILED(hrc))
+ return hrc;
+ }
+
+ HRESULT rc = S_OK;
+ bool fBeganSavingState = false;
+ bool fTaskCreationFailed = false;
+
+ do
+ {
+ ComPtr<IProgress> pProgress;
+ Bstr stateFilePath;
+
+ /*
+ * request a saved state file path from the server
+ * (this will set the machine state to Saving on the server to block
+ * others from accessing this machine)
+ */
+ rc = mControl->BeginSavingState(pProgress.asOutParam(),
+ stateFilePath.asOutParam());
+ if (FAILED(rc))
+ break;
+
+ fBeganSavingState = true;
+
+ /* sync the state with the server */
+ setMachineStateLocally(MachineState_Saving);
+
+ /* ensure the directory for the saved state file exists */
+ {
+ Utf8Str dir = stateFilePath;
+ dir.stripFilename();
+ if (!RTDirExists(dir.c_str()))
+ {
+ int vrc = RTDirCreateFullPath(dir.c_str(), 0700);
+ if (RT_FAILURE(vrc))
+ {
+ rc = setError(VBOX_E_FILE_ERROR,
+ tr("Could not create a directory '%s' to save the state to (%Rrc)"),
+ dir.c_str(), vrc);
+ break;
+ }
+ }
+ }
+
+ /* Create a task object early to ensure mpUVM protection is successful. */
+ std::auto_ptr<VMSaveTask> task(new VMSaveTask(this, pProgress,
+ stateFilePath,
+ lastMachineState,
+ aReason));
+ rc = task->rc();
+ /*
+ * If we fail here it means a PowerDown() call happened on another
+ * thread while we were doing Pause() (which releases the Console lock).
+ * We assign PowerDown() a higher precedence than SaveState(),
+ * therefore just return the error to the caller.
+ */
+ if (FAILED(rc))
+ {
+ fTaskCreationFailed = true;
+ break;
+ }
+
+ /* create a thread to wait until the VM state is saved */
+ int vrc = RTThreadCreate(NULL, Console::saveStateThread, (void *)task.get(),
+ 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave");
+ if (RT_FAILURE(vrc))
+ {
+ rc = setError(E_FAIL, "Could not create VMSave thread (%Rrc)", vrc);
+ break;
+ }
+
+ /* task is now owned by saveStateThread(), so release it */
+ task.release();
+
+ /* return the progress to the caller */
+ pProgress.queryInterfaceTo(aProgress);
+ } while (0);
+
+ if (FAILED(rc) && !fTaskCreationFailed)
+ {
+ /* preserve existing error info */
+ ErrorInfoKeeper eik;
+
+ if (fBeganSavingState)
+ {
+ /*
+ * cancel the requested save state procedure.
+ * This will reset the machine state to the state it had right
+ * before calling mControl->BeginSavingState().
+ */
+ mControl->EndSavingState(eik.getResultCode(), eik.getText().raw());
+ }
+
+ if (lastMachineState == MachineState_Running)
+ {
+ /* restore the paused state if appropriate */
+ setMachineStateLocally(MachineState_Paused);
+ /* restore the running state if appropriate */
+ SafeVMPtr ptrVM(this);
+ if (ptrVM.isOk())
+ {
+ alock.release();
+ VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_STATE_RESTORED);
+ alock.acquire();
+ }
+ }
+ else
+ setMachineStateLocally(lastMachineState);
+ }
+
+ LogFlowThisFunc(("rc=%Rhrc\n", rc));
+ LogFlowThisFuncLeave();
+ return rc;
+}
+
+/**
* Gets called by Session::UpdateMachineState()
* (IInternalSessionControl::updateMachineState()).
*
@@ -5933,7 +6037,8 @@ void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
* @note Locks this object for writing.
*/
#endif
-void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative, BOOL needsHostCursor)
+void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative,
+ BOOL supportsMT, BOOL needsHostCursor)
{
LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
supportsAbsolute, supportsRelative, needsHostCursor));
@@ -5954,14 +6059,13 @@ void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelati
}
#endif
- fireMouseCapabilityChangedEvent(mEventSource, supportsAbsolute, supportsRelative, needsHostCursor);
+ fireMouseCapabilityChangedEvent(mEventSource, supportsAbsolute, supportsRelative, supportsMT, needsHostCursor);
}
void Console::onStateChange(MachineState_T machineState)
{
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
-
fireStateChangedEvent(mEventSource, machineState);
}
@@ -6098,25 +6202,27 @@ HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
////////////////////////////////////////////////////////////////////////////////
/**
- * Increases the usage counter of the mpVM pointer. Guarantees that
- * VMR3Destroy() will not be called on it at least until releaseVMCaller()
- * is called.
+ * Increases the usage counter of the mpUVM pointer.
+ *
+ * Guarantees that VMR3Destroy() will not be called on it at least until
+ * releaseVMCaller() is called.
*
- * If this method returns a failure, the caller is not allowed to use mpVM
- * and may return the failed result code to the upper level. This method sets
- * the extended error info on failure if \a aQuiet is false.
+ * If this method returns a failure, the caller is not allowed to use mpUVM and
+ * may return the failed result code to the upper level. This method sets the
+ * extended error info on failure if \a aQuiet is false.
*
* Setting \a aQuiet to true is useful for methods that don't want to return
* the failed result code to the caller when this method fails (e.g. need to
- * silently check for the mpVM availability).
+ * silently check for the mpUVM availability).
*
- * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
+ * When mpUVM is NULL but \a aAllowNullVM is true, a corresponding error will be
* returned instead of asserting. Having it false is intended as a sanity check
- * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
+ * for methods that have checked mMachineState and expect mpUVM *NOT* to be
+ * NULL.
*
* @param aQuiet true to suppress setting error info
- * @param aAllowNullVM true to accept mpVM being NULL and return a failure
- * (otherwise this method will assert if mpVM is NULL)
+ * @param aAllowNullVM true to accept mpUVM being NULL and return a failure
+ * (otherwise this method will assert if mpUVM is NULL)
*
* @note Locks this object for writing.
*/
@@ -6153,8 +6259,10 @@ HRESULT Console::addVMCaller(bool aQuiet /* = false */,
}
/**
- * Decreases the usage counter of the mpVM pointer. Must always complete
- * the addVMCaller() call after the mpVM pointer is no more necessary.
+ * Decreases the usage counter of the mpUVM pointer.
+ *
+ * Must always complete the addVMCaller() call after the mpUVM pointer is no
+ * more necessary.
*
* @note Locks this object for writing.
*/
@@ -6178,9 +6286,8 @@ void Console::releaseVMCaller()
}
-HRESULT Console::safeVMPtrRetainer(PVM *a_ppVM, PUVM *a_ppUVM, bool a_Quiet)
+HRESULT Console::safeVMPtrRetainer(PUVM *a_ppUVM, bool a_Quiet)
{
- *a_ppVM = NULL;
*a_ppUVM = NULL;
AutoCaller autoCaller(this);
@@ -6192,13 +6299,13 @@ HRESULT Console::safeVMPtrRetainer(PVM *a_ppVM, PUVM *a_ppUVM, bool a_Quiet)
*/
if (mVMDestroying) /* powerDown() is waiting for all callers to finish */
return a_Quiet
- ? E_ACCESSDENIED
- : setError(E_ACCESSDENIED, tr("The virtual machine is being powered down"));
+ ? E_ACCESSDENIED
+ : setError(E_ACCESSDENIED, tr("The virtual machine is being powered down"));
PUVM pUVM = mpUVM;
if (!pUVM)
return a_Quiet
- ? E_ACCESSDENIED
- : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
+ ? E_ACCESSDENIED
+ : setError(E_ACCESSDENIED, tr("The virtual machine is powered off"));
/*
* Retain a reference to the user mode VM handle and get the global handle.
@@ -6207,28 +6314,17 @@ HRESULT Console::safeVMPtrRetainer(PVM *a_ppVM, PUVM *a_ppUVM, bool a_Quiet)
if (cRefs == UINT32_MAX)
return a_Quiet
? E_ACCESSDENIED
- : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
-
- PVM pVM = VMR3GetVM(pUVM);
- if (!pVM)
- {
- VMR3ReleaseUVM(pUVM);
- return a_Quiet
- ? E_ACCESSDENIED
- : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
- }
+ : setError(E_ACCESSDENIED, tr("The virtual machine is powered off"));
/* done */
- *a_ppVM = pVM;
*a_ppUVM = pUVM;
return S_OK;
}
-void Console::safeVMPtrReleaser(PVM *a_ppVM, PUVM *a_ppUVM)
+void Console::safeVMPtrReleaser(PUVM *a_ppUVM)
{
- if (*a_ppVM && *a_ppUVM)
+ if (*a_ppUVM)
VMR3ReleaseUVM(*a_ppUVM);
- *a_ppVM = NULL;
*a_ppUVM = NULL;
}
@@ -6298,7 +6394,7 @@ HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
char szError[RTPATH_MAX + 128];
int vrc = com::VBoxLogRelCreate("VM", logFile.c_str(),
RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_RESTRICT_GROUPS,
- "all all.restrict default.unrestricted",
+ "all all.restrict -default.restrict",
"VBOX_RELEASE_LOG", RTLOGDEST_FILE,
32768 /* cMaxEntriesPerGroup */,
0 /* cHistory */, 0 /* uHistoryFileTime */,
@@ -6328,8 +6424,8 @@ HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
*/
HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
{
+
LogFlowThisFuncEnter();
- LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
CheckComArgOutPointerValid(aProgress);
@@ -6338,22 +6434,34 @@ HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
HRESULT rc = S_OK;
ComObjPtr<Progress> pPowerupProgress;
bool fBeganPoweringUp = false;
+ LONG cOperations = 1;
+ LONG ulTotalOperationsWeight = 1;
+
try
{
+
if (Global::IsOnlineOrTransient(mMachineState))
throw setError(VBOX_E_INVALID_VM_STATE,
tr("The virtual machine is already running or busy (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
+ /* Set up release logging as early as possible after the check if
+ * there is already a running VM which we shouldn't disturb. */
+ rc = consoleInitReleaseLog(mMachine);
+ if (FAILED(rc))
+ throw rc;
+
/* test and clear the TeleporterEnabled property */
BOOL fTeleporterEnabled;
rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
if (FAILED(rc))
throw rc;
+
#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
if (fTeleporterEnabled)
{
@@ -6384,101 +6492,28 @@ HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
progressDesc = tr("Fault Tolerance syncing of remote virtual machine");
else
progressDesc = tr("Starting virtual machine");
- if ( mMachineState == MachineState_Saved
- || (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
- rc = pPowerupProgress->init(static_cast<IConsole *>(this),
- progressDesc.raw(),
- FALSE /* aCancelable */);
- else
- if (fTeleporterEnabled)
- rc = pPowerupProgress->init(static_cast<IConsole *>(this),
- progressDesc.raw(),
- TRUE /* aCancelable */,
- 3 /* cOperations */,
- 10 /* ulTotalOperationsWeight */,
- Bstr(tr("Teleporting virtual machine")).raw(),
- 1 /* ulFirstOperationWeight */,
- NULL);
- else
- if (fFaultToleranceSyncEnabled)
- rc = pPowerupProgress->init(static_cast<IConsole *>(this),
- progressDesc.raw(),
- TRUE /* aCancelable */,
- 3 /* cOperations */,
- 10 /* ulTotalOperationsWeight */,
- Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
- 1 /* ulFirstOperationWeight */,
- NULL);
- if (FAILED(rc))
- throw rc;
-
- /* Tell VBoxSVC and Machine about the progress object so they can
- combine/proxy it to any openRemoteSession caller. */
- LogFlowThisFunc(("Calling BeginPowerUp...\n"));
- rc = mControl->BeginPowerUp(pPowerupProgress);
- if (FAILED(rc))
- {
- LogFlowThisFunc(("BeginPowerUp failed\n"));
- throw rc;
- }
- fBeganPoweringUp = true;
+ Bstr savedStateFile;
- /** @todo this code prevents starting a VM with unavailable bridged
- * networking interface. The only benefit is a slightly better error
- * message, which should be moved to the driver code. This is the
- * only reason why I left the code in for now. The driver allows
- * unavailable bridged networking interfaces in certain circumstances,
- * and this is sabotaged by this check. The VM will initially have no
- * network connectivity, but the user can fix this at runtime. */
-#if 0
- /* the network cards will undergo a quick consistency check */
- for (ULONG slot = 0;
- slot < maxNetworkAdapters;
- ++slot)
+ /*
+ * Saved VMs will have to prove that their saved states seem kosher.
+ */
+ if (mMachineState == MachineState_Saved)
{
- ComPtr<INetworkAdapter> pNetworkAdapter;
- mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
- BOOL enabled = FALSE;
- pNetworkAdapter->COMGETTER(Enabled)(&enabled);
- if (!enabled)
- continue;
-
- NetworkAttachmentType_T netattach;
- pNetworkAdapter->COMGETTER(AttachmentType)(&netattach);
- switch (netattach)
- {
- case NetworkAttachmentType_Bridged:
- {
- /* a valid host interface must have been set */
- Bstr hostif;
- pNetworkAdapter->COMGETTER(HostInterface)(hostif.asOutParam());
- if (hostif.isEmpty())
- {
- throw setError(VBOX_E_HOST_ERROR,
- tr("VM cannot start because host interface networking requires a host interface name to be set"));
- }
- ComPtr<IVirtualBox> pVirtualBox;
- mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
- ComPtr<IHost> pHost;
- pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
- ComPtr<IHostNetworkInterface> pHostInterface;
- if (!SUCCEEDED(pHost->FindHostNetworkInterfaceByName(hostif.raw(),
- pHostInterface.asOutParam())))
- {
- throw setError(VBOX_E_HOST_ERROR,
- tr("VM cannot start because the host interface '%ls' does not exist"),
- hostif.raw());
- }
- break;
- }
- default:
- break;
- }
+ rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
+ if (FAILED(rc))
+ throw rc;
+ ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
+ int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
+ if (RT_FAILURE(vrc))
+ throw setError(VBOX_E_FILE_ERROR,
+ tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
+ savedStateFile.raw(), vrc);
}
-#endif // 0
- /* Read console data stored in the saved state file (if not yet done) */
+ /* Read console data, including console shared folders, stored in the
+ * saved state file (if not yet done).
+ */
rc = loadDataFromSavedState();
if (FAILED(rc))
throw rc;
@@ -6518,39 +6553,8 @@ HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
}
}
- Bstr savedStateFile;
-
- /*
- * Saved VMs will have to prove that their saved states seem kosher.
- */
- if (mMachineState == MachineState_Saved)
- {
- rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
- if (FAILED(rc))
- throw rc;
- ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
- int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
- if (RT_FAILURE(vrc))
- throw setError(VBOX_E_FILE_ERROR,
- tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
- savedStateFile.raw(), vrc);
- }
-
- LogFlowThisFunc(("Checking if canceled...\n"));
- BOOL fCanceled;
- rc = pPowerupProgress->COMGETTER(Canceled)(&fCanceled);
- if (FAILED(rc))
- throw rc;
- if (fCanceled)
- {
- LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
- throw setError(E_FAIL, tr("Powerup was canceled"));
- }
- LogFlowThisFunc(("Not canceled yet.\n"));
-
- /* setup task object and thread to carry out the operation
- * asynchronously */
-
+ /* Setup task object and thread to carry out the operaton
+ * Asycnhronously */
std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, pPowerupProgress));
ComAssertComRCRetRC(task->rc());
@@ -6628,9 +6632,9 @@ HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
else
LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
- rc = consoleInitReleaseLog(mMachine);
- if (FAILED(rc))
- throw rc;
+ /* setup task object and thread to carry out the operation
+ * asynchronously */
+
#ifdef VBOX_WITH_EXTPACK
mptrExtPackManager->dumpAllToReleaseLog();
#endif
@@ -6649,15 +6653,11 @@ HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
uint32_t fCoreFlags = 0;
if ( coreDumpReplaceSys.isEmpty() == false
&& Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
- {
fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
- }
if ( coreDumpLive.isEmpty() == false
&& Utf8Str(coreDumpLive).toUInt32() == 1)
- {
fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
- }
Utf8Str strDumpDir(coreDumpDir);
const char *pszDumpDir = strDumpDir.c_str();
@@ -6685,29 +6685,143 @@ HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
}
#endif
- /* pass the progress object to the caller if requested */
- if (aProgress)
- {
- if (task->hardDiskProgresses.size() == 0)
+
+ // If there is immutable drive the process that.
+ VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
+ if (aProgress && progresses.size() > 0){
+
+ for (VMPowerUpTask::ProgressList::const_iterator it = progresses.begin(); it != progresses.end(); ++it)
{
- /* there are no other operations to track, return the powerup
- * progress only */
- pPowerupProgress.queryInterfaceTo(aProgress);
+ ++cOperations;
+ ulTotalOperationsWeight += 1;
}
- else
+ rc = pPowerupProgress->init(static_cast<IConsole *>(this),
+ progressDesc.raw(),
+ TRUE, // Cancelable
+ cOperations,
+ ulTotalOperationsWeight,
+ Bstr(tr("Starting Hard Disk operations")).raw(),
+ 1,
+ NULL);
+ AssertComRCReturnRC(rc);
+ }
+ else if ( mMachineState == MachineState_Saved
+ || (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
+ {
+ rc = pPowerupProgress->init(static_cast<IConsole *>(this),
+ progressDesc.raw(),
+ FALSE /* aCancelable */);
+ }
+ else if (fTeleporterEnabled)
+ {
+ rc = pPowerupProgress->init(static_cast<IConsole *>(this),
+ progressDesc.raw(),
+ TRUE /* aCancelable */,
+ 3 /* cOperations */,
+ 10 /* ulTotalOperationsWeight */,
+ Bstr(tr("Teleporting virtual machine")).raw(),
+ 1 /* ulFirstOperationWeight */,
+ NULL);
+ }
+ else if (fFaultToleranceSyncEnabled)
+ {
+ rc = pPowerupProgress->init(static_cast<IConsole *>(this),
+ progressDesc.raw(),
+ TRUE /* aCancelable */,
+ 3 /* cOperations */,
+ 10 /* ulTotalOperationsWeight */,
+ Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
+ 1 /* ulFirstOperationWeight */,
+ NULL);
+ }
+
+ if (FAILED(rc))
+ throw rc;
+
+ /* Tell VBoxSVC and Machine about the progress object so they can
+ combine/proxy it to any openRemoteSession caller. */
+ LogFlowThisFunc(("Calling BeginPowerUp...\n"));
+ rc = mControl->BeginPowerUp(pPowerupProgress);
+ if (FAILED(rc))
+ {
+ LogFlowThisFunc(("BeginPowerUp failed\n"));
+ throw rc;
+ }
+ fBeganPoweringUp = true;
+
+ LogFlowThisFunc(("Checking if canceled...\n"));
+ BOOL fCanceled;
+ rc = pPowerupProgress->COMGETTER(Canceled)(&fCanceled);
+ if (FAILED(rc))
+ throw rc;
+
+ if (fCanceled)
+ {
+ LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
+ throw setError(E_FAIL, tr("Powerup was canceled"));
+ }
+ LogFlowThisFunc(("Not canceled yet.\n"));
+
+ /** @todo this code prevents starting a VM with unavailable bridged
+ * networking interface. The only benefit is a slightly better error
+ * message, which should be moved to the driver code. This is the
+ * only reason why I left the code in for now. The driver allows
+ * unavailable bridged networking interfaces in certain circumstances,
+ * and this is sabotaged by this check. The VM will initially have no
+ * network connectivity, but the user can fix this at runtime. */
+#if 0
+ /* the network cards will undergo a quick consistency check */
+ for (ULONG slot = 0;
+ slot < maxNetworkAdapters;
+ ++slot)
+ {
+ ComPtr<INetworkAdapter> pNetworkAdapter;
+ mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
+ BOOL enabled = FALSE;
+ pNetworkAdapter->COMGETTER(Enabled)(&enabled);
+ if (!enabled)
+ continue;
+
+ NetworkAttachmentType_T netattach;
+ pNetworkAdapter->COMGETTER(AttachmentType)(&netattach);
+ switch (netattach)
{
- /* create a combined progress object */
- ComObjPtr<CombinedProgress> pProgress;
- pProgress.createObject();
- VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
- progresses.push_back(ComPtr<IProgress> (pPowerupProgress));
- rc = pProgress->init(static_cast<IConsole *>(this),
- progressDesc.raw(), progresses.begin(),
- progresses.end());
- AssertComRCReturnRC(rc);
- pProgress.queryInterfaceTo(aProgress);
+ case NetworkAttachmentType_Bridged:
+ {
+ /* a valid host interface must have been set */
+ Bstr hostif;
+ pNetworkAdapter->COMGETTER(HostInterface)(hostif.asOutParam());
+ if (hostif.isEmpty())
+ {
+ throw setError(VBOX_E_HOST_ERROR,
+ tr("VM cannot start because host interface networking requires a host interface name to be set"));
+ }
+ ComPtr<IVirtualBox> pVirtualBox;
+ mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
+ ComPtr<IHost> pHost;
+ pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
+ ComPtr<IHostNetworkInterface> pHostInterface;
+ if (!SUCCEEDED(pHost->FindHostNetworkInterfaceByName(hostif.raw(),
+ pHostInterface.asOutParam())))
+ {
+ throw setError(VBOX_E_HOST_ERROR,
+ tr("VM cannot start because the host interface '%ls' does not exist"),
+ hostif.raw());
+ }
+ break;
+ }
+ default:
+ break;
}
}
+#endif // 0
+
+ /* setup task object and thread to carry out the operation
+ * asynchronously */
+ if (aProgress){
+ rc = pPowerupProgress.queryInterfaceTo(aProgress);
+ AssertComRCReturnRC(rc);
+ }
int vrc = RTThreadCreate(NULL, Console::powerUpThread,
(void *)task.get(), 0,
@@ -6724,7 +6838,7 @@ HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
if (mMachineState == MachineState_Saved)
setMachineState(MachineState_Restoring);
else if (fTeleporterEnabled)
- setMachineState(MachineState_TeleportingIn);
+ setMachineState(MachineState_TeleportingIn);
else if (enmFaultToleranceState == FaultToleranceState_Standby)
setMachineState(MachineState_FaultTolerantSyncing);
else
@@ -6771,7 +6885,7 @@ HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
*
* Calling it in situations other than the above will cause unexpected behavior.
*
- * Note that this method should be the only one that destroys mpVM and sets it
+ * Note that this method should be the only one that destroys mpUVM and sets it
* to NULL.
*
* @param aProgress Progress object to run (may be NULL).
@@ -6878,8 +6992,8 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
/* ----------------------------------------------------------------------
- * Now, wait for all mpVM callers to finish their work if there are still
- * some on other threads. NO methods that need mpVM (or initiate other calls
+ * Now, wait for all mpUVM callers to finish their work if there are still
+ * some on other threads. NO methods that need mpUVM (or initiate other calls
* that need it) may be called after this point
* ---------------------------------------------------------------------- */
@@ -6892,8 +7006,7 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
if (mVMZeroCallersSem == NIL_RTSEMEVENT)
RTSemEventCreate(&mVMZeroCallersSem);
- LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
- mVMCallers));
+ LogFlowThisFunc(("Waiting for mpUVM callers (%d) to drop to zero...\n", mVMCallers));
alock.release();
@@ -6920,7 +7033,7 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
{
LogFlowThisFunc(("Powering off the VM...\n"));
alock.release();
- vrc = VMR3PowerOff(VMR3GetVM(pUVM));
+ vrc = VMR3PowerOff(pUVM);
#ifdef VBOX_WITH_EXTPACK
mptrExtPackManager->callAllVmPowerOffHooks(this, VMR3GetVM(pUVM));
#endif
@@ -6957,26 +7070,20 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
* on failure (this will most likely fail too, but what to do?..) */
if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
{
- /* If the machine has an USB controller, release all USB devices
+ /* If the machine has a USB controller, release all USB devices
* (symmetric to the code in captureUSBDevices()) */
- bool fHasUSBController = false;
+ if (mfVMHasUsbController)
{
- PPDMIBASE pBase;
- vrc = PDMR3QueryLun(VMR3GetVM(pUVM), "usb-ohci", 0, 0, &pBase);
- if (RT_SUCCESS(vrc))
- {
- fHasUSBController = true;
- alock.release();
- detachAllUSBDevices(false /* aDone */);
- alock.acquire();
- }
+ alock.release();
+ detachAllUSBDevices(false /* aDone */);
+ alock.acquire();
}
- /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
+ /* Now we've got to destroy the VM as well. (mpUVM is not valid beyond
* this point). We release the lock before calling VMR3Destroy() because
* it will result into calling destructors of drivers associated with
* Console children which may in turn try to lock Console (e.g. by
- * instantiating SafeVMPtr to access mpVM). It's safe here because
+ * instantiating SafeVMPtr to access mpUVM). It's safe here because
* mVMDestroying is set which should prevent any activity. */
/* Set mpUVM to NULL early just in case if some old code is not using
@@ -6988,7 +7095,7 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
alock.release();
- vrc = VMR3Destroy(VMR3GetVM(pUVM));
+ vrc = VMR3Destroy(pUVM);
/* take the lock again */
alock.acquire();
@@ -7000,7 +7107,7 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
if (RT_SUCCESS(vrc))
{
LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
- mMachineState));
+ mMachineState));
/* Note: the Console-level machine state change happens on the
* VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
* powerDown() is called from EMT (i.e. from vmstateChangeCallback()
@@ -7020,7 +7127,7 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
}
/* Complete the detaching of the USB devices. */
- if (fHasUSBController)
+ if (mfVMHasUsbController)
{
alock.release();
detachAllUSBDevices(true /* aDone */);
@@ -7046,7 +7153,7 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
* something like Stopping, so most Console methods will return an error
* to the caller.
*/
- if (mpUVM != NULL)
+ if (pUVM != NULL)
VMR3ReleaseUVM(pUVM);
else
mVMDestroying = false;
@@ -7281,8 +7388,9 @@ HRESULT Console::fetchSharedFolders(BOOL aGlobal)
}
catch (HRESULT rc2)
{
+ rc = rc2;
if (online)
- setVMRuntimeErrorCallbackF(ptrVM, this, 0, "BrokenSharedFolder",
+ setVMRuntimeErrorCallbackF(0, "BrokenSharedFolder",
N_("Broken shared folder!"));
}
@@ -7326,7 +7434,7 @@ bool Console::findOtherSharedFolder(const Utf8Str &strName,
* @param aName Shared folder name.
* @param aHostPath Shared folder path.
*
- * @note Must be called from under AutoVMCaller and when mpVM != NULL!
+ * @note Must be called from under AutoVMCaller and when mpUVM != NULL!
* @note Doesn't lock anything.
*/
HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderData &aData)
@@ -7356,14 +7464,15 @@ HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderDa
aData.m_strHostPath.c_str(),
hostPathFull,
sizeof(hostPathFull));
+
+ bool fMissing = false;
if (RT_FAILURE(vrc))
return setError(E_INVALIDARG,
tr("Invalid shared folder path: '%s' (%Rrc)"),
aData.m_strHostPath.c_str(), vrc);
if (!RTPathExists(hostPathFull))
- return setError(E_INVALIDARG,
- tr("Shared folder path '%s' does not exist on the host"),
- aData.m_strHostPath.c_str());
+ fMissing = true;
+
/* Check whether the path is full (absolute) */
if (RTPathCompare(aData.m_strHostPath.c_str(), hostPathFull) != 0)
return setError(E_INVALIDARG,
@@ -7409,7 +7518,9 @@ HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderDa
parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
parms[2].u.uint32 = (aData.m_fWritable ? SHFL_ADD_MAPPING_F_WRITABLE : 0)
| (aData.m_fAutoMount ? SHFL_ADD_MAPPING_F_AUTOMOUNT : 0)
- | (fSymlinksCreate ? SHFL_ADD_MAPPING_F_CREATE_SYMLINKS : 0);
+ | (fSymlinksCreate ? SHFL_ADD_MAPPING_F_CREATE_SYMLINKS : 0)
+ | (fMissing ? SHFL_ADD_MAPPING_F_MISSING : 0)
+ ;
vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
SHFL_FN_ADD_MAPPING,
@@ -7422,6 +7533,11 @@ HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderDa
tr("Could not create a shared folder '%s' mapped to '%s' (%Rrc)"),
strName.c_str(), aData.m_strHostPath.c_str(), vrc);
+ if (fMissing)
+ return setError(E_INVALIDARG,
+ tr("Shared folder path '%s' does not exist on the host"),
+ aData.m_strHostPath.c_str());
+
return S_OK;
}
@@ -7430,7 +7546,7 @@ HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderDa
*
* @param aName Shared folder name.
*
- * @note Must be called from under AutoVMCaller and when mpVM != NULL!
+ * @note Must be called from under AutoVMCaller and when mpUVM != NULL!
* @note Doesn't lock anything.
*/
HRESULT Console::removeSharedFolder(const Utf8Str &strName)
@@ -7474,31 +7590,18 @@ HRESULT Console::removeSharedFolder(const Utf8Str &strName)
return S_OK;
}
-/**
- * VM state callback function. Called by the VMM
- * using its state machine states.
- *
- * Primarily used to handle VM initiated power off, suspend and state saving,
- * but also for doing termination completed work (VMSTATE_TERMINATE).
+/** @callback_method_impl{FNVMATSTATE}
*
- * In general this function is called in the context of the EMT.
- *
- * @param aVM The VM handle.
- * @param aState The new state.
- * @param aOldState The old state.
- * @param aUser The user argument (pointer to the Console object).
- *
- * @note Locks the Console object for writing.
+ * @note Locks the Console object for writing.
+ * @remarks The @a pUVM parameter can be NULL in one case where powerUpThread()
+ * calls after the VM was destroyed.
*/
-DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
- VMSTATE aState,
- VMSTATE aOldState,
- void *aUser)
+DECLCALLBACK(void) Console::vmstateChangeCallback(PUVM pUVM, VMSTATE enmState, VMSTATE enmOldState, void *pvUser)
{
- LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
- VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
+ LogFlowFunc(("Changing state from %s to %s (pUVM=%p)\n",
+ VMR3GetStateName(enmOldState), VMR3GetStateName(enmState), pUVM));
- Console *that = static_cast<Console *>(aUser);
+ Console *that = static_cast<Console *>(pvUser);
AssertReturnVoid(that);
AutoCaller autoCaller(that);
@@ -7510,17 +7613,34 @@ DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
AssertReturnVoid( autoCaller.isOk()
|| autoCaller.state() == InUninit);
- switch (aState)
+ switch (enmState)
{
/*
* The VM has terminated
*/
case VMSTATE_OFF:
{
+#ifdef VBOX_WITH_GUEST_PROPS
+ if (that->isResetTurnedIntoPowerOff())
+ {
+ Bstr strPowerOffReason;
+
+ if (that->mfPowerOffCausedByReset)
+ strPowerOffReason = Bstr("Reset");
+ else
+ strPowerOffReason = Bstr("PowerOff");
+
+ that->mMachine->DeleteGuestProperty(Bstr("/VirtualBox/HostInfo/VMPowerOffReason").raw());
+ that->mMachine->SetGuestProperty(Bstr("/VirtualBox/HostInfo/VMPowerOffReason").raw(),
+ strPowerOffReason.raw(), Bstr("RDONLYGUEST").raw());
+ that->mMachine->SaveSettings();
+ }
+#endif
+
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
if (that->mVMStateChangeCallbackDisabled)
- break;
+ return;
/* Do we still think that it is running? It may happen if this is a
* VM-(guest-)initiated shutdown/poweroff.
@@ -7536,7 +7656,11 @@ DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
{
LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
- /* prevent powerDown() from calling VMR3PowerOff() again */
+ /*
+ * Prevent powerDown() from calling VMR3PowerOff() again if this was called from
+ * the power off state change.
+ * When called from the Reset state make sure to call VMR3PowerOff() first.
+ */
Assert(that->mVMPoweredOff == false);
that->mVMPoweredOff = true;
@@ -7554,11 +7678,10 @@ DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
/* Setup task object and thread to carry out the operation
* asynchronously (if we call powerDown() right here but there
- * is one or more mpVM callers (added with addVMCaller()) we'll
+ * is one or more mpUVM callers (added with addVMCaller()) we'll
* deadlock).
*/
- std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that,
- pProgress));
+ std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that, pProgress));
/* If creating a task failed, this can currently mean one of
* two: either Console::uninit() has been called just a ms
@@ -7569,14 +7692,14 @@ DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
if (!task->isOk())
{
LogFlowFunc(("Console is already being uninitialized.\n"));
- break;
+ return;
}
int vrc = RTThreadCreate(NULL, Console::powerDownThread,
- (void *) task.get(), 0,
+ (void *)task.get(), 0,
RTTHREADTYPE_MAIN_WORKER, 0,
"VMPwrDwn");
- AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
+ AssertMsgRCReturnVoid(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
/* task is now owned by powerDownThread(), so release it */
task.release();
@@ -7598,12 +7721,12 @@ DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
if (that->mVMStateChangeCallbackDisabled)
break;
- /* Terminate host interface networking. If aVM is NULL, we've been
+ /* Terminate host interface networking. If pUVM is NULL, we've been
* manually called from powerUpThread() either before calling
* VMR3Create() or after VMR3Create() failed, so no need to touch
* networking.
*/
- if (aVM)
+ if (pUVM)
that->powerDownHostInterfaces();
/* From now on the machine is officially powered down or remains in
@@ -7718,7 +7841,7 @@ DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
break;
default:
- AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
+ AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(enmOldState), VMR3GetStateName(enmState) ));
that->setMachineState(MachineState_Paused);
break;
}
@@ -7727,9 +7850,9 @@ DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
case VMSTATE_RUNNING:
{
- if ( aOldState == VMSTATE_POWERING_ON
- || aOldState == VMSTATE_RESUMING
- || aOldState == VMSTATE_RUNNING_FT)
+ if ( enmOldState == VMSTATE_POWERING_ON
+ || enmOldState == VMSTATE_RESUMING
+ || enmOldState == VMSTATE_RUNNING_FT)
{
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
@@ -7738,15 +7861,15 @@ DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
Assert( ( ( that->mMachineState == MachineState_Starting
|| that->mMachineState == MachineState_Paused)
- && aOldState == VMSTATE_POWERING_ON)
+ && enmOldState == VMSTATE_POWERING_ON)
|| ( ( that->mMachineState == MachineState_Restoring
|| that->mMachineState == MachineState_TeleportingIn
|| that->mMachineState == MachineState_Paused
|| that->mMachineState == MachineState_Saving
)
- && aOldState == VMSTATE_RESUMING)
+ && enmOldState == VMSTATE_RESUMING)
|| ( that->mMachineState == MachineState_FaultTolerantSyncing
- && aOldState == VMSTATE_RUNNING_FT));
+ && enmOldState == VMSTATE_RUNNING_FT));
that->setMachineState(MachineState_Running);
}
@@ -7757,12 +7880,12 @@ DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
case VMSTATE_RUNNING_LS:
AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
|| that->mMachineState == MachineState_Teleporting,
- ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
+ ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(enmOldState), VMR3GetStateName(enmState) ));
break;
case VMSTATE_RUNNING_FT:
AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
- ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
+ ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(enmOldState), VMR3GetStateName(enmState) ));
break;
case VMSTATE_FATAL_ERROR:
@@ -7859,15 +7982,15 @@ void Console::changeDragAndDropMode(DragAndDropMode_T aDragAndDropMode)
LogRel(("Drag'n'drop mode: Off\n"));
parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_OFF;
break;
- case ClipboardMode_GuestToHost:
+ case DragAndDropMode_GuestToHost:
LogRel(("Drag'n'drop mode: Guest to Host\n"));
parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST;
break;
- case ClipboardMode_HostToGuest:
+ case DragAndDropMode_HostToGuest:
LogRel(("Drag'n'drop mode: Host to Guest\n"));
parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST;
break;
- case ClipboardMode_Bidirectional:
+ case DragAndDropMode_Bidirectional:
LogRel(("Drag'n'drop mode: Bidirectional\n"));
parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL;
break;
@@ -7934,10 +8057,10 @@ HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
AssertComRCReturnRC(hrc);
Assert(portVersion == 1 || portVersion == 2);
- int vrc = VMR3ReqCallWait(ptrVM, 0 /* idDstCpu (saved state, see #6232) */,
- (PFNRT)usbAttachCallback, 9,
- this, ptrVM.raw(), aHostDevice, uuid.raw(), fRemote, Address.c_str(), pvRemoteBackend, portVersion, aMaskedIfs);
-
+ int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), 0 /* idDstCpu (saved state, see #6232) */,
+ (PFNRT)usbAttachCallback, 9,
+ this, ptrVM.rawUVM(), aHostDevice, uuid.raw(), fRemote,
+ Address.c_str(), pvRemoteBackend, portVersion, aMaskedIfs);
if (RT_SUCCESS(vrc))
{
/* Create a OUSBDevice and add it to the device list */
@@ -7962,17 +8085,13 @@ HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
switch (vrc)
{
case VERR_VUSB_NO_PORTS:
- hrc = setError(E_FAIL,
- tr("Failed to attach the USB device. (No available ports on the USB controller)."));
+ hrc = setError(E_FAIL, tr("Failed to attach the USB device. (No available ports on the USB controller)."));
break;
case VERR_VUSB_USBFS_PERMISSION:
- hrc = setError(E_FAIL,
- tr("Not permitted to open the USB device, check usbfs options"));
+ hrc = setError(E_FAIL, tr("Not permitted to open the USB device, check usbfs options"));
break;
default:
- hrc = setError(E_FAIL,
- tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
- vrc);
+ hrc = setError(E_FAIL, tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"), vrc);
break;
}
}
@@ -7991,7 +8110,8 @@ HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
*/
//static
DECLCALLBACK(int)
-Console::usbAttachCallback(Console *that, PVM pVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, void *pvRemoteBackend, USHORT aPortVersion, ULONG aMaskedIfs)
+Console::usbAttachCallback(Console *that, PUVM pUVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote,
+ const char *aAddress, void *pvRemoteBackend, USHORT aPortVersion, ULONG aMaskedIfs)
{
LogFlowFuncEnter();
LogFlowFunc(("that={%p} aUuid={%RTuuid}\n", that, aUuid));
@@ -7999,7 +8119,7 @@ Console::usbAttachCallback(Console *that, PVM pVM, IUSBDevice *aHostDevice, PCRT
AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
AssertReturn(!that->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
- int vrc = PDMR3USBCreateProxyDevice(pVM, aUuid, aRemote, aAddress, pvRemoteBackend,
+ int vrc = PDMR3UsbCreateProxyDevice(pUVM, aUuid, aRemote, aAddress, pvRemoteBackend,
aPortVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
LogFlowFunc(("vrc=%Rrc\n", vrc));
LogFlowFuncLeave();
@@ -8025,7 +8145,7 @@ HRESULT Console::detachUSBDevice(const ComObjPtr<OUSBDevice> &aHostDevice)
return ptrVM.rc();
/* if the device is attached, then there must at least one USB hub. */
- AssertReturn(PDMR3USBHasHub(ptrVM), E_FAIL);
+ AssertReturn(PDMR3UsbHasHub(ptrVM.rawUVM()), E_FAIL);
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
@@ -8049,9 +8169,9 @@ HRESULT Console::detachUSBDevice(const ComObjPtr<OUSBDevice> &aHostDevice)
}
alock.release();
- int vrc = VMR3ReqCallWait(ptrVM, 0 /* idDstCpu (saved state, see #6232) */,
- (PFNRT)usbDetachCallback, 5,
- this, ptrVM.raw(), pUuid);
+ int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), 0 /* idDstCpu (saved state, see #6232) */,
+ (PFNRT)usbDetachCallback, 5,
+ this, ptrVM.rawUVM(), pUuid);
if (RT_SUCCESS(vrc))
{
LogFlowFunc(("Detached device {%RTuuid}\n", pUuid));
@@ -8067,6 +8187,7 @@ HRESULT Console::detachUSBDevice(const ComObjPtr<OUSBDevice> &aHostDevice)
/**
* USB device detach callback used by DetachUSBDevice().
+ *
* Note that DetachUSBDevice() doesn't return until this callback is executed,
* so we don't use AutoCaller and don't care about reference counters of
* interface pointers passed in.
@@ -8075,7 +8196,7 @@ HRESULT Console::detachUSBDevice(const ComObjPtr<OUSBDevice> &aHostDevice)
*/
//static
DECLCALLBACK(int)
-Console::usbDetachCallback(Console *that, PVM pVM, PCRTUUID aUuid)
+Console::usbDetachCallback(Console *that, PUVM pUVM, PCRTUUID aUuid)
{
LogFlowFuncEnter();
LogFlowFunc(("that={%p} aUuid={%RTuuid}\n", that, aUuid));
@@ -8083,7 +8204,7 @@ Console::usbDetachCallback(Console *that, PVM pVM, PCRTUUID aUuid)
AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
AssertReturn(!that->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
- int vrc = PDMR3USBDetachDevice(pVM, aUuid);
+ int vrc = PDMR3UsbDetachDevice(pUVM, aUuid);
LogFlowFunc(("vrc=%Rrc\n", vrc));
LogFlowFuncLeave();
@@ -8134,7 +8255,7 @@ HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
* Set/obtain the tap interface.
*/
struct ifreq IfReq;
- memset(&IfReq, 0, sizeof(IfReq));
+ RT_ZERO(IfReq);
/* The name of the TAP interface we are using */
Bstr tapDeviceName;
rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
@@ -8150,10 +8271,7 @@ HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
{
/* If we are using a static TAP device then try to open it. */
Utf8Str str(tapDeviceName);
- if (str.length() <= sizeof(IfReq.ifr_name))
- strcpy(IfReq.ifr_name, str.c_str());
- else
- memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
+ RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), str.c_str()); /** @todo bitch about names which are too long... */
IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
if (rcVBox != 0)
@@ -8387,13 +8505,13 @@ HRESULT Console::powerDownHostInterfaces()
* Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
* and VMR3Teleport.
*
- * @param pVM The VM handle.
+ * @param pUVM The user mode VM handle.
* @param uPercent Completion percentage (0-100).
* @param pvUser Pointer to an IProgress instance.
* @return VINF_SUCCESS.
*/
/*static*/
-DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
+DECLCALLBACK(int) Console::stateProgressCallback(PUVM pUVM, unsigned uPercent, void *pvUser)
{
IProgress *pProgress = static_cast<IProgress *>(pvUser);
@@ -8401,6 +8519,7 @@ DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, voi
if (pProgress)
pProgress->SetCurrentOperationProgress(uPercent);
+ NOREF(pUVM);
return VINF_SUCCESS;
}
@@ -8411,7 +8530,7 @@ DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, voi
* object here...
*/
/*static*/ DECLCALLBACK(void)
-Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
+Console::genericVMSetErrorCallback(PUVM pUVM, void *pvUser, int rc, RT_SRC_POS_DECL,
const char *pszErrorFmt, va_list va)
{
Utf8Str *pErrorText = (Utf8Str *)pvUser;
@@ -8429,14 +8548,17 @@ Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DEC
*pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
va_end(va2);
+
+ NOREF(pUVM);
}
/**
* VM runtime error callback function.
* See VMSetRuntimeError for the detailed description of parameters.
*
- * @param pVM The VM handle.
- * @param pvUser The user argument.
+ * @param pUVM The user mode VM handle. Ignored, so passing NULL
+ * is fine.
+ * @param pvUser The user argument, pointer to the Console instance.
* @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
* @param pszErrorId Error ID string.
* @param pszFormat Error message format string.
@@ -8444,7 +8566,7 @@ Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DEC
* @thread EMT.
*/
/* static */ DECLCALLBACK(void)
-Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
+Console::setVMRuntimeErrorCallback(PUVM pUVM, void *pvUser, uint32_t fFlags,
const char *pszErrorId,
const char *pszFormat, va_list va)
{
@@ -8457,21 +8579,20 @@ Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
Utf8Str message(pszFormat, va);
LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
- fFatal, pszErrorId, message.c_str()));
+ fFatal, pszErrorId, message.c_str()));
- that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
- Bstr(message).raw());
+ that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(), Bstr(message).raw());
- LogFlowFuncLeave();
+ LogFlowFuncLeave(); NOREF(pUVM);
}
/**
* Captures USB devices that match filters of the VM.
* Called at VM startup.
*
- * @param pVM The VM handle.
+ * @param pUVM The VM handle.
*/
-HRESULT Console::captureUSBDevices(PVM pVM)
+HRESULT Console::captureUSBDevices(PUVM pUVM)
{
LogFlowThisFunc(("\n"));
@@ -8479,11 +8600,9 @@ HRESULT Console::captureUSBDevices(PVM pVM)
AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- /* If the machine has an USB controller, ask the USB proxy service to
+ /* If the machine has a USB controller, ask the USB proxy service to
* capture devices */
- PPDMIBASE pBase;
- int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
- if (RT_SUCCESS(vrc))
+ if (mfVMHasUsbController)
{
/* release the lock before calling Host in VBoxSVC since Host may call
* us back from under its lock (e.g. onUSBDeviceAttach()) which would
@@ -8493,13 +8612,8 @@ HRESULT Console::captureUSBDevices(PVM pVM)
HRESULT hrc = mControl->AutoCaptureUSBDevices();
ComAssertComRCRetRC(hrc);
}
- else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
- || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
- vrc = VINF_SUCCESS;
- else
- AssertRC(vrc);
- return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
+ return S_OK;
}
@@ -8702,10 +8816,10 @@ void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *p
*/
static void faultToleranceProgressCancelCallback(void *pvUser)
{
- PVM pVM = (PVM)pvUser;
+ PUVM pUVM = (PUVM)pvUser;
- if (pVM)
- FTMR3CancelStandby(pVM);
+ if (pUVM)
+ FTMR3CancelStandby(pUVM);
}
/**
@@ -8771,6 +8885,9 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
{
HRESULT rc2 = (*it)->WaitForCompletion(-1);
AssertComRC(rc2);
+
+ rc = task->mProgress->SetNextOperation(BstrFmt(tr("Disk Image Reset Operation - Immutable Image")).raw(), 1);
+ AssertComRCReturnRC(rc);
}
/*
@@ -8844,21 +8961,20 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
/*
* Create the VM
- */
- PVM pVM;
- /*
- * release the lock since EMT will call Console. It's safe because
- * mMachineState is either Starting or Restoring state here.
+ *
+ * Note! Release the lock since EMT will call Console. It's safe because
+ * mMachineState is either Starting or Restoring state here.
*/
alock.release();
+ PVM pVM;
vrc = VMR3Create(cCpus,
pConsole->mpVmm2UserMethods,
Console::genericVMSetErrorCallback,
&task->mErrorMsg,
task->mConfigConstructor,
static_cast<Console *>(pConsole),
- &pVM);
+ &pVM, NULL);
alock.acquire();
@@ -8872,14 +8988,14 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
/*
* Register our load/save state file handlers
*/
- vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
+ vrc = SSMR3RegisterExternal(pConsole->mpUVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
NULL, NULL, NULL,
NULL, saveStateFileExec, NULL,
NULL, loadStateFileExec, NULL,
static_cast<Console *>(pConsole));
AssertRCBreak(vrc);
- vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pVM);
+ vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pConsole->mpUVM);
AssertRC(vrc);
if (RT_FAILURE(vrc))
break;
@@ -8909,10 +9025,11 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
if (FAILED(rc))
{
ErrorInfoKeeper eik;
- setVMRuntimeErrorCallbackF(pVM, pConsole, 0, "BrokenSharedFolder",
- N_("The shared folder '%s' could not be set up: %ls.\n"
- "The shared folder setup will not be complete. It is recommended to power down the virtual machine and fix the shared folder settings while the machine is not running"),
- it->first.c_str(), eik.getText().raw());
+ pConsole->setVMRuntimeErrorCallbackF(0, "BrokenSharedFolder",
+ N_("The shared folder '%s' could not be set up: %ls.\n"
+ "The shared folder setup will not be complete. It is recommended to power down the virtual "
+ "machine and fix the shared folder settings while the machine is not running"),
+ it->first.c_str(), eik.getText().raw());
}
}
if (FAILED(rc))
@@ -8928,8 +9045,9 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
/*
* Capture USB devices.
*/
- rc = pConsole->captureUSBDevices(pVM);
- if (FAILED(rc)) break;
+ rc = pConsole->captureUSBDevices(pConsole->mpUVM);
+ if (FAILED(rc))
+ break;
/* Load saved state? */
if (task->mSavedStateFile.length())
@@ -8937,7 +9055,7 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
LogFlowFunc(("Restoring saved state from '%s'...\n",
task->mSavedStateFile.c_str()));
- vrc = VMR3LoadFromFile(pVM,
+ vrc = VMR3LoadFromFile(pConsole->mpUVM,
task->mSavedStateFile.c_str(),
Console::stateProgressCallback,
static_cast<IProgress *>(task->mProgress));
@@ -8954,7 +9072,7 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
#endif
if (RT_SUCCESS(vrc))
- vrc = VMR3Resume(pVM);
+ vrc = VMR3Resume(pConsole->mpUVM, VMRESUMEREASON_STATE_RESTORED);
AssertLogRelRC(vrc);
}
}
@@ -8962,7 +9080,7 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
/* Power off in case we failed loading or resuming the VM */
if (RT_FAILURE(vrc))
{
- int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
+ int vrc2 = VMR3PowerOff(pConsole->mpUVM); AssertLogRelRC(vrc2);
#ifdef VBOX_WITH_EXTPACK
pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
#endif
@@ -8972,12 +9090,12 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
{
/* -> ConsoleImplTeleporter.cpp */
bool fPowerOffOnFailure;
- rc = pConsole->teleporterTrg(VMR3GetUVM(pVM), pMachine, &task->mErrorMsg, task->mStartPaused,
+ rc = pConsole->teleporterTrg(pConsole->mpUVM, pMachine, &task->mErrorMsg, task->mStartPaused,
task->mProgress, &fPowerOffOnFailure);
if (FAILED(rc) && fPowerOffOnFailure)
{
ErrorInfoKeeper eik;
- int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
+ int vrc2 = VMR3PowerOff(pConsole->mpUVM); AssertLogRelRC(vrc2);
#ifdef VBOX_WITH_EXTPACK
pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
#endif
@@ -9001,7 +9119,7 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
if (SUCCEEDED(rc))
rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
}
- if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
+ if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pConsole->mpUVM))
{
if (SUCCEEDED(rc))
{
@@ -9015,7 +9133,7 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
#endif
if (RT_SUCCESS(vrc))
- vrc = FTMR3PowerOn(pVM,
+ vrc = FTMR3PowerOn(pConsole->mpUVM,
task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
uInterval,
pszAddress,
@@ -9038,7 +9156,7 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
#endif
if (RT_SUCCESS(vrc))
- vrc = VMR3PowerOn(pVM);
+ vrc = VMR3PowerOn(pConsole->mpUVM);
AssertLogRelRC(vrc);
}
@@ -9068,7 +9186,7 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
* be sticky but our error callback isn't.
*/
alock.release();
- VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
+ VMR3AtErrorDeregister(pConsole->mpUVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
/** @todo register another VMSetError callback? */
alock.acquire();
}
@@ -9127,8 +9245,7 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
ErrorInfoKeeper eik;
Assert(pConsole->mpUVM == NULL);
- vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
- pConsole);
+ vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING, pConsole);
}
/*
@@ -9168,8 +9285,8 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
/**
* Reconfigures a medium attachment (part of taking or deleting an online snapshot).
*
- * @param pConsole Reference to the console object.
- * @param pVM The VM handle.
+ * @param pThis Reference to the console object.
+ * @param pUVM The VM handle.
* @param lInstance The instance of the controller.
* @param pcszDevice The name of the controller type.
* @param enmBus The storage bus type of the controller.
@@ -9182,8 +9299,8 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
* @return VBox status code.
*/
/* static */
-DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
- PVM pVM,
+DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pThis,
+ PUVM pUVM,
const char *pcszDevice,
unsigned uInstance,
StorageBus_T enmBus,
@@ -9196,13 +9313,11 @@ DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
MachineState_T aMachineState,
HRESULT *phrc)
{
- LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
+ LogFlowFunc(("pUVM=%p aMediumAtt=%p phrc=%p\n", pUVM, aMediumAtt, phrc));
- int rc;
HRESULT hrc;
Bstr bstr;
*phrc = S_OK;
-#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
/* Ignore attachments other than hard disks, since at the moment they are
@@ -9214,33 +9329,37 @@ DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
/* Determine the base path for the device instance. */
PCFGMNODE pCtlInst;
- pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
+ pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
/* Update the device instance configuration. */
- rc = pConsole->configMediumAttachment(pCtlInst,
- pcszDevice,
- uInstance,
- enmBus,
- fUseHostIOCache,
- fBuiltinIOCache,
- fSetupMerge,
- uMergeSource,
- uMergeTarget,
- aMediumAtt,
- aMachineState,
- phrc,
- true /* fAttachDetach */,
- false /* fForceUnmount */,
- false /* fHotplug */,
- pVM,
- NULL /* paLedDevType */);
- /** @todo this dumps everything attached to this device instance, which
- * is more than necessary. Dumping the changed LUN would be enough. */
- CFGMR3Dump(pCtlInst);
- RC_CHECK();
+ PCFGMNODE pLunL0 = NULL;
+ int rc = pThis->configMediumAttachment(pCtlInst,
+ pcszDevice,
+ uInstance,
+ enmBus,
+ fUseHostIOCache,
+ fBuiltinIOCache,
+ fSetupMerge,
+ uMergeSource,
+ uMergeTarget,
+ aMediumAtt,
+ aMachineState,
+ phrc,
+ true /* fAttachDetach */,
+ false /* fForceUnmount */,
+ false /* fHotplug */,
+ pUVM,
+ NULL /* paLedDevType */,
+ &pLunL0);
+ /* Dump the changed LUN if possible, dump the complete device otherwise */
+ CFGMR3Dump(pLunL0 ? pLunL0 : pCtlInst);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("rc=%Rrc\n", rc));
+ return rc;
+ }
-#undef RC_CHECK
#undef H
LogFlowFunc(("Returns success\n"));
@@ -9253,7 +9372,7 @@ DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
static void takesnapshotProgressCancelCallback(void *pvUser)
{
PUVM pUVM = (PUVM)pvUser;
- SSMR3Cancel(VMR3GetVM(pUVM));
+ SSMR3Cancel(pUVM);
}
/**
@@ -9308,13 +9427,13 @@ DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
fBeganTakingSnapshot = true;
- /*
- * state file is non-null only when the VM is paused
- * (i.e. creating a snapshot online)
- */
- bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
- || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
- if (!f)
+ /* Check sanity: for offline snapshots there must not be a saved state
+ * file name. All other combinations are valid (even though online
+ * snapshots without saved state file seems inconsistent - there are
+ * some exotic use cases, which need to be explicitly enabled, see the
+ * code of SessionMachine::BeginTakingSnapshot. */
+ if ( !pTask->fTakingSnapshotOnline
+ && !pTask->bstrSavedStateFile.isEmpty())
throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
/* sync the state with the server */
@@ -9326,31 +9445,38 @@ DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
// STEP 3: save the VM state (if online)
if (pTask->fTakingSnapshotOnline)
{
- Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
-
+ int vrc;
SafeVMPtr ptrVM(that);
if (!ptrVM.isOk())
throw ptrVM.rc();
pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
pTask->ulMemSize); // operation weight, same as computed when setting up progress object
- pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
+ if (!pTask->bstrSavedStateFile.isEmpty())
+ {
+ Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
- alock.release();
- LogFlowFunc(("VMR3Save...\n"));
- int vrc = VMR3Save(ptrVM,
+ pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
+
+ alock.release();
+ LogFlowFunc(("VMR3Save...\n"));
+ vrc = VMR3Save(ptrVM.rawUVM(),
strSavedStateFile.c_str(),
true /*fContinueAfterwards*/,
Console::stateProgressCallback,
static_cast<IProgress *>(pTask->mProgress),
&fSuspenededBySave);
- alock.acquire();
- if (RT_FAILURE(vrc))
- throw setErrorStatic(E_FAIL,
- tr("Failed to save the machine state to '%s' (%Rrc)"),
- strSavedStateFile.c_str(), vrc);
+ alock.acquire();
+ if (RT_FAILURE(vrc))
+ throw setErrorStatic(E_FAIL,
+ tr("Failed to save the machine state to '%s' (%Rrc)"),
+ strSavedStateFile.c_str(), vrc);
+
+ pTask->mProgress->setCancelCallback(NULL, NULL);
+ }
+ else
+ LogRel(("Console: skipped saving state as part of online snapshot\n"));
- pTask->mProgress->setCancelCallback(NULL, NULL);
if (!pTask->mProgress->notifyPointOfNoReturn())
throw setErrorStatic(E_FAIL, tr("Canceled"));
that->mptrCancelableProgress.setNull();
@@ -9415,23 +9541,11 @@ DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
* don't release the lock since reconfigureMediumAttachment
* isn't going to need the Console lock.
*/
- vrc = VMR3ReqCallWait(ptrVM,
- VMCPUID_ANY,
- (PFNRT)reconfigureMediumAttachment,
- 13,
- that,
- ptrVM.raw(),
- pcszDevice,
- lInstance,
- enmBus,
- fUseHostIOCache,
- fBuiltinIOCache,
- false /* fSetupMerge */,
- 0 /* uMergeSource */,
- 0 /* uMergeTarget */,
- atts[i],
- that->mMachineState,
- &rc);
+ vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY,
+ (PFNRT)reconfigureMediumAttachment, 13,
+ that, ptrVM.rawUVM(), pcszDevice, lInstance, enmBus, fUseHostIOCache,
+ fBuiltinIOCache, false /* fSetupMerge */, 0 /* uMergeSource */,
+ 0 /* uMergeTarget */, atts[i], that->mMachineState, &rc);
if (RT_FAILURE(vrc))
throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
if (FAILED(rc))
@@ -9488,7 +9602,7 @@ DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
LogFlowFunc(("VMR3Resume...\n"));
SafeVMPtr ptrVM(that);
alock.release();
- int vrc = VMR3Resume(ptrVM);
+ int vrc = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_STATE_SAVED);
alock.acquire();
if (RT_FAILURE(vrc))
{
@@ -9546,7 +9660,7 @@ DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
LogFlowFunc(("VMR3Resume (on failure)...\n"));
SafeVMPtr ptrVM(that);
alock.release();
- int vrc = VMR3Resume(ptrVM); AssertLogRelRC(vrc);
+ int vrc = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_STATE_SAVED); AssertLogRelRC(vrc);
alock.acquire();
if (RT_FAILURE(vrc))
that->setMachineState(MachineState_Paused);
@@ -9606,7 +9720,7 @@ DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
bool fSuspenededBySave;
- int vrc = VMR3Save(task->mpVM,
+ int vrc = VMR3Save(task->mpUVM,
task->mSavedStateFile.c_str(),
false, /*fContinueAfterwards*/
Console::stateProgressCallback,
@@ -9758,6 +9872,18 @@ Console::vmm2User_NotifyPdmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM)
VirtualBoxBase::uninitializeComForThread();
}
+/**
+ * @interface_method_impl{VMM2USERMETHODS,pfnNotifyResetTurnedIntoPowerOff}
+ */
+/*static*/ DECLCALLBACK(void)
+Console::vmm2User_NotifyResetTurnedIntoPowerOff(PCVMM2USERMETHODS pThis, PUVM pUVM)
+{
+ Console *pConsole = ((MYVMM2USERMETHODS *)pThis)->pConsole;
+ NOREF(pUVM);
+
+ pConsole->mfPowerOffCausedByReset = true;
+}
+
@@ -9802,14 +9928,14 @@ typedef struct DRVMAINSTATUS
*/
DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
{
- PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, ILedConnectors));
- if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
+ PDRVMAINSTATUS pThis = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, ILedConnectors));
+ if (iLUN >= pThis->iFirstLUN && iLUN <= pThis->iLastLUN)
{
PPDMLED pLed;
- int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
+ int rc = pThis->pLedPorts->pfnQueryStatusLed(pThis->pLedPorts, iLUN, &pLed);
if (RT_FAILURE(rc))
pLed = NULL;
- ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
+ ASMAtomicWritePtr(&pThis->papLeds[iLUN - pThis->iFirstLUN], pLed);
Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
}
}
@@ -9824,17 +9950,17 @@ DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface,
*/
DECLCALLBACK(int) Console::drvStatus_MediumEjected(PPDMIMEDIANOTIFY pInterface, unsigned uLUN)
{
- PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, IMediaNotify));
- PPDMDRVINS pDrvIns = pData->pDrvIns;
+ PDRVMAINSTATUS pThis = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, IMediaNotify));
+ PPDMDRVINS pDrvIns = pThis->pDrvIns;
LogFunc(("uLUN=%d\n", uLUN));
- if (pData->pmapMediumAttachments)
+ if (pThis->pmapMediumAttachments)
{
- AutoWriteLock alock(pData->pConsole COMMA_LOCKVAL_SRC_POS);
+ AutoWriteLock alock(pThis->pConsole COMMA_LOCKVAL_SRC_POS);
ComPtr<IMediumAttachment> pMediumAtt;
- Utf8Str devicePath = Utf8StrFmt("%s/LUN#%u", pData->pszDeviceInstance, uLUN);
- Console::MediumAttachmentMap::const_iterator end = pData->pmapMediumAttachments->end();
- Console::MediumAttachmentMap::const_iterator it = pData->pmapMediumAttachments->find(devicePath);
+ Utf8Str devicePath = Utf8StrFmt("%s/LUN#%u", pThis->pszDeviceInstance, uLUN);
+ Console::MediumAttachmentMap::const_iterator end = pThis->pmapMediumAttachments->end();
+ Console::MediumAttachmentMap::const_iterator it = pThis->pmapMediumAttachments->find(devicePath);
if (it != end)
pMediumAtt = it->second;
Assert(!pMediumAtt.isNull());
@@ -9853,15 +9979,15 @@ DECLCALLBACK(int) Console::drvStatus_MediumEjected(PPDMIMEDIANOTIFY pInterface,
alock.release();
ComPtr<IMediumAttachment> pNewMediumAtt;
- rc = pData->pConsole->mControl->EjectMedium(pMediumAtt, pNewMediumAtt.asOutParam());
+ rc = pThis->pConsole->mControl->EjectMedium(pMediumAtt, pNewMediumAtt.asOutParam());
if (SUCCEEDED(rc))
- fireMediumChangedEvent(pData->pConsole->mEventSource, pNewMediumAtt);
+ fireMediumChangedEvent(pThis->pConsole->mEventSource, pNewMediumAtt);
alock.acquire();
if (pNewMediumAtt != pMediumAtt)
{
- pData->pmapMediumAttachments->erase(devicePath);
- pData->pmapMediumAttachments->insert(std::make_pair(devicePath, pNewMediumAtt));
+ pThis->pmapMediumAttachments->erase(devicePath);
+ pThis->pmapMediumAttachments->insert(std::make_pair(devicePath, pNewMediumAtt));
}
}
}
@@ -9893,15 +10019,15 @@ DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, co
*/
DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
{
- PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
- LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
+ LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
- if (pData->papLeds)
+ if (pThis->papLeds)
{
- unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
+ unsigned iLed = pThis->iLastLUN - pThis->iFirstLUN + 1;
while (iLed-- > 0)
- ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
+ ASMAtomicWriteNullPtr(&pThis->papLeds[iLed]);
}
}
@@ -9913,9 +10039,9 @@ DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
*/
DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
- PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
- LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
+ LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
/*
* Validate configuration.
@@ -9930,36 +10056,36 @@ DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCf
* Data.
*/
pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
- pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
- pData->IMediaNotify.pfnEjected = Console::drvStatus_MediumEjected;
- pData->pDrvIns = pDrvIns;
- pData->pszDeviceInstance = NULL;
+ pThis->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
+ pThis->IMediaNotify.pfnEjected = Console::drvStatus_MediumEjected;
+ pThis->pDrvIns = pDrvIns;
+ pThis->pszDeviceInstance = NULL;
/*
* Read config.
*/
- int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
+ int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pThis->papLeds);
if (RT_FAILURE(rc))
{
AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
return rc;
}
- rc = CFGMR3QueryPtrDef(pCfg, "pmapMediumAttachments", (void **)&pData->pmapMediumAttachments, NULL);
+ rc = CFGMR3QueryPtrDef(pCfg, "pmapMediumAttachments", (void **)&pThis->pmapMediumAttachments, NULL);
if (RT_FAILURE(rc))
{
AssertMsgFailed(("Configuration error: Failed to query the \"pmapMediumAttachments\" value! rc=%Rrc\n", rc));
return rc;
}
- if (pData->pmapMediumAttachments)
+ if (pThis->pmapMediumAttachments)
{
- rc = CFGMR3QueryStringAlloc(pCfg, "DeviceInstance", &pData->pszDeviceInstance);
+ rc = CFGMR3QueryStringAlloc(pCfg, "DeviceInstance", &pThis->pszDeviceInstance);
if (RT_FAILURE(rc))
{
AssertMsgFailed(("Configuration error: Failed to query the \"DeviceInstance\" value! rc=%Rrc\n", rc));
return rc;
}
- rc = CFGMR3QueryPtr(pCfg, "pConsole", (void **)&pData->pConsole);
+ rc = CFGMR3QueryPtr(pCfg, "pConsole", (void **)&pThis->pConsole);
if (RT_FAILURE(rc))
{
AssertMsgFailed(("Configuration error: Failed to query the \"pConsole\" value! rc=%Rrc\n", rc));
@@ -9967,26 +10093,26 @@ DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCf
}
}
- rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
+ rc = CFGMR3QueryU32(pCfg, "First", &pThis->iFirstLUN);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
- pData->iFirstLUN = 0;
+ pThis->iFirstLUN = 0;
else if (RT_FAILURE(rc))
{
AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
return rc;
}
- rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
+ rc = CFGMR3QueryU32(pCfg, "Last", &pThis->iLastLUN);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
- pData->iLastLUN = 0;
+ pThis->iLastLUN = 0;
else if (RT_FAILURE(rc))
{
AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
return rc;
}
- if (pData->iFirstLUN > pData->iLastLUN)
+ if (pThis->iFirstLUN > pThis->iLastLUN)
{
- AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
+ AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pThis->iFirstLUN, pThis->iLastLUN));
return VERR_GENERAL_FAILURE;
}
@@ -9994,12 +10120,12 @@ DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCf
* Get the ILedPorts interface of the above driver/device and
* query the LEDs we want.
*/
- pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
- AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
+ pThis->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
+ AssertMsgReturn(pThis->pLedPorts, ("Configuration error: No led ports interface above!\n"),
VERR_PDM_MISSING_INTERFACE_ABOVE);
- for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
- Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
+ for (unsigned i = pThis->iFirstLUN; i <= pThis->iLastLUN; ++i)
+ Console::drvStatus_UnitChanged(&pThis->ILedConnectors, i);
return VINF_SUCCESS;
}
diff --git a/src/VBox/Main/src-client/ConsoleImpl2.cpp b/src/VBox/Main/src-client/ConsoleImpl2.cpp
index 515ba2bd..78feac8f 100644
--- a/src/VBox/Main/src-client/ConsoleImpl2.cpp
+++ b/src/VBox/Main/src-client/ConsoleImpl2.cpp
@@ -9,7 +9,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;
@@ -46,6 +46,7 @@
#include "AutoCaller.h"
#include "Logging.h"
+#include <iprt/base64.h>
#include <iprt/buildconfig.h>
#include <iprt/ctype.h>
#include <iprt/dir.h>
@@ -113,15 +114,24 @@
# endif
#endif /* VBOX_WITH_NETFLT */
-#include "DHCPServerRunner.h"
+#include "NetworkServiceRunner.h"
#include "BusAssignmentManager.h"
#ifdef VBOX_WITH_EXTPACK
# include "ExtPackManagerImpl.h"
#endif
-
#if defined(RT_OS_DARWIN)
-
# include "IOKit/IOKitLib.h"
+#endif
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static Utf8Str *GetExtraDataBoth(IVirtualBox *pVirtualBox, IMachine *pMachine, const char *pszName, Utf8Str *pStrValue);
+
+
+
+#if defined(RT_OS_DARWIN)
static int DarwinSmcKey(char *pabKey, uint32_t cbKey)
{
@@ -160,7 +170,7 @@ static int DarwinSmcKey(char *pabKey, uint32_t cbKey)
for (int i = 0; i < 2; i++)
{
- inputStruct.key = (uint32_t)((i == 0) ? 'OSK0' : 'OSK1');
+ inputStruct.key = (uint32_t)(i == 0 ? 'OSK0' : 'OSK1');
kr = IOConnectCallStructMethod((mach_port_t)port,
(uint32_t)2,
(const void *)&inputStruct,
@@ -246,20 +256,18 @@ static int findEfiRom(IVirtualBox* vbox, FirmwareType_T aFirmwareType, Utf8Str *
return VINF_SUCCESS;
}
-static int getSmcDeviceKey(IMachine *pMachine, BSTR *aKey, bool *pfGetKeyFromRealSMC)
+/**
+ * @throws HRESULT on extra data retrival error.
+ */
+static int getSmcDeviceKey(IVirtualBox *pVirtualBox, IMachine *pMachine, Utf8Str *pStrKey, bool *pfGetKeyFromRealSMC)
{
*pfGetKeyFromRealSMC = false;
/*
* The extra data takes precedence (if non-zero).
*/
- HRESULT hrc = pMachine->GetExtraData(Bstr("VBoxInternal2/SmcDeviceKey").raw(),
- aKey);
- if (FAILED(hrc))
- return Global::vboxStatusCodeFromCOM(hrc);
- if ( SUCCEEDED(hrc)
- && *aKey
- && **aKey)
+ GetExtraDataBoth(pVirtualBox, pMachine, "VBoxInternal2/SmcDeviceKey", pStrKey);
+ if (pStrKey->isNotEmpty())
return VINF_SUCCESS;
#ifdef RT_OS_DARWIN
@@ -270,7 +278,8 @@ static int getSmcDeviceKey(IMachine *pMachine, BSTR *aKey, bool *pfGetKeyFromRea
int rc = DarwinSmcKey(abKeyBuf, sizeof(abKeyBuf));
if (SUCCEEDED(rc))
{
- Bstr(abKeyBuf).detachTo(aKey);
+ *pStrKey = abKeyBuf;
+ *pfGetKeyFromRealSMC = true;
return rc;
}
LogRel(("Warning: DarwinSmcKey failed with rc=%Rrc!\n", rc));
@@ -296,10 +305,10 @@ static int getSmcDeviceKey(IMachine *pMachine, BSTR *aKey, bool *pfGetKeyFromRea
char szProdName[256];
szProdName[0] = '\0';
RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szProdName, sizeof(szProdName));
- if ( ( !strncmp(szProdName, "Mac", 3)
- || !strncmp(szProdName, "iMac", 4)
- || !strncmp(szProdName, "iMac", 4)
- || !strncmp(szProdName, "Xserve", 6)
+ if ( ( !strncmp(szProdName, RT_STR_TUPLE("Mac"))
+ || !strncmp(szProdName, RT_STR_TUPLE("iMac"))
+ || !strncmp(szProdName, RT_STR_TUPLE("iMac"))
+ || !strncmp(szProdName, RT_STR_TUPLE("Xserve"))
)
&& !strchr(szProdName, ' ') /* no spaces */
&& RT_C_IS_DIGIT(szProdName[strlen(szProdName) - 1]) /* version number */
@@ -464,10 +473,137 @@ static void RemoveConfigValue(PCFGMNODE pNode,
throw ConfigError("CFGMR3RemoveValue", vrc, pcszName);
}
+/**
+ * Gets an extra data value, consulting both machine and global extra data.
+ *
+ * @throws HRESULT on failure
+ * @returns pStrValue for the callers convenience.
+ * @param pVirtualBox Pointer to the IVirtualBox interface.
+ * @param pMachine Pointer to the IMachine interface.
+ * @param pszName The value to get.
+ * @param pStrValue Where to return it's value (empty string if not
+ * found).
+ */
+static Utf8Str *GetExtraDataBoth(IVirtualBox *pVirtualBox, IMachine *pMachine, const char *pszName, Utf8Str *pStrValue)
+{
+ pStrValue->setNull();
+
+ Bstr bstrName(pszName);
+ Bstr bstrValue;
+ HRESULT hrc = pMachine->GetExtraData(bstrName.raw(), bstrValue.asOutParam());
+ if (FAILED(hrc))
+ throw hrc;
+ if (bstrValue.isEmpty())
+ {
+ hrc = pVirtualBox->GetExtraData(bstrName.raw(), bstrValue.asOutParam());
+ if (FAILED(hrc))
+ throw hrc;
+ }
+
+ if (bstrValue.isNotEmpty())
+ *pStrValue = bstrValue;
+ return pStrValue;
+}
+
+
+/** Helper that finds out the next HBA port used
+ */
+static LONG GetNextUsedPort(LONG aPortUsed[30], LONG lBaseVal, uint32_t u32Size)
+{
+ LONG lNextPortUsed = 30;
+ for (size_t j = 0; j < u32Size; ++j)
+ {
+ if ( aPortUsed[j] > lBaseVal
+ && aPortUsed[j] <= lNextPortUsed)
+ lNextPortUsed = aPortUsed[j];
+ }
+ return lNextPortUsed;
+}
+
+#define MAX_BIOS_LUN_COUNT 4
+
+static int SetBiosDiskInfo(ComPtr<IMachine> pMachine, PCFGMNODE pCfg, PCFGMNODE pBiosCfg,
+ Bstr controllerName, const char * const s_apszBiosConfig[4])
+{
+ HRESULT hrc;
+#define MAX_DEVICES 30
+#define H() AssertMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_GENERAL_FAILURE)
+
+ LONG lPortLUN[MAX_BIOS_LUN_COUNT];
+ LONG lPortUsed[MAX_DEVICES];
+ uint32_t u32HDCount = 0;
+
+ /* init to max value */
+ lPortLUN[0] = MAX_DEVICES;
+
+ com::SafeIfaceArray<IMediumAttachment> atts;
+ hrc = pMachine->GetMediumAttachmentsOfController(controllerName.raw(),
+ ComSafeArrayAsOutParam(atts)); H();
+ size_t uNumAttachments = atts.size();
+ if (uNumAttachments > MAX_DEVICES)
+ {
+ LogRel(("Number of Attachments > Max=%d.\n", uNumAttachments));
+ uNumAttachments = MAX_DEVICES;
+ }
+
+ /* Find the relevant ports/IDs, i.e the ones to which a HD is attached. */
+ for (size_t j = 0; j < uNumAttachments; ++j)
+ {
+ IMediumAttachment *pMediumAtt = atts[j];
+ LONG lPortNum = 0;
+ hrc = pMediumAtt->COMGETTER(Port)(&lPortNum); H();
+ if (SUCCEEDED(hrc))
+ {
+ DeviceType_T lType;
+ hrc = pMediumAtt->COMGETTER(Type)(&lType); H();
+ if (SUCCEEDED(hrc) && lType == DeviceType_HardDisk)
+ {
+ /* find min port number used for HD */
+ if (lPortNum < lPortLUN[0])
+ lPortLUN[0] = lPortNum;
+ lPortUsed[u32HDCount++] = lPortNum;
+ LogFlowFunc(("HD port Count=%d\n", u32HDCount));
+ }
+
+ /* Configure the hotpluggable flag for the port. */
+ BOOL fHotPluggable = FALSE;
+ hrc = pMediumAtt->COMGETTER(HotPluggable)(&fHotPluggable); H();
+ if (SUCCEEDED(hrc))
+ {
+ PCFGMNODE pPortCfg;
+ char szName[24];
+ RTStrPrintf(szName, sizeof(szName), "Port%d", lPortNum);
+
+ InsertConfigNode(pCfg, szName, &pPortCfg);
+ InsertConfigInteger(pPortCfg, "Hotpluggable", fHotPluggable ? 1 : 0);
+ }
+ }
+ }
+
+
+ /* Pick only the top 4 used HD Ports as CMOS doesn't have space
+ * to save details for all 30 ports
+ */
+ uint32_t u32MaxPortCount = MAX_BIOS_LUN_COUNT;
+ if (u32HDCount < MAX_BIOS_LUN_COUNT)
+ u32MaxPortCount = u32HDCount;
+ for (size_t j = 1; j < u32MaxPortCount; j++)
+ lPortLUN[j] = GetNextUsedPort(lPortUsed,
+ lPortLUN[j-1],
+ u32HDCount);
+ if (pBiosCfg)
+ {
+ for (size_t j = 0; j < u32MaxPortCount; j++)
+ {
+ InsertConfigInteger(pBiosCfg, s_apszBiosConfig[j], lPortLUN[j]);
+ LogFlowFunc(("Top %d HBA ports = %s, %d\n", j, s_apszBiosConfig[j], lPortLUN[j]));
+ }
+ }
+ return VINF_SUCCESS;
+}
+
#ifdef VBOX_WITH_PCI_PASSTHROUGH
-HRESULT Console::attachRawPCIDevices(PVM pVM,
- BusAssignmentManager *BusMgr,
- PCFGMNODE pDevices)
+HRESULT Console::attachRawPCIDevices(PUVM pUVM, BusAssignmentManager *pBusMgr, PCFGMNODE pDevices)
{
HRESULT hrc = S_OK;
PCFGMNODE pInst, pCfg, pLunL0, pLunL1;
@@ -491,14 +627,12 @@ HRESULT Console::attachRawPCIDevices(PVM pVM,
# ifdef VBOX_WITH_EXTPACK
static const char *s_pszPCIRawExtPackName = "Oracle VM VirtualBox Extension Pack";
if (!mptrExtPackManager->isExtPackUsable(s_pszPCIRawExtPackName))
- {
/* Always fatal! */
- return VMSetError(pVM, VERR_NOT_FOUND, RT_SRC_POS,
+ return VMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
N_("Implementation of the PCI passthrough framework not found!\n"
"The VM cannot be started. To fix this problem, either "
"install the '%s' or disable PCI passthrough via VBoxManage"),
s_pszPCIRawExtPackName);
- }
# endif
PCFGMNODE pBridges = CFGMR3GetChild(pDevices, "ich9pcibridge");
@@ -520,7 +654,7 @@ HRESULT Console::attachRawPCIDevices(PVM pVM,
int iBridgesMissed = 0;
int iBase = GuestPCIAddress.miBus - 1;
- while (!BusMgr->hasPCIDevice("ich9pcibridge", iBase) && iBase > 0)
+ while (!pBusMgr->hasPCIDevice("ich9pcibridge", iBase) && iBase > 0)
{
iBridgesMissed++; iBase--;
}
@@ -530,7 +664,7 @@ HRESULT Console::attachRawPCIDevices(PVM pVM,
{
InsertConfigNode(pBridges, Utf8StrFmt("%d", iBase + iBridge).c_str(), &pInst);
InsertConfigInteger(pInst, "Trusted", 1);
- hrc = BusMgr->assignPCIDevice("ich9pcibridge", pInst);
+ hrc = pBusMgr->assignPCIDevice("ich9pcibridge", pInst);
}
}
}
@@ -583,7 +717,7 @@ HRESULT Console::attachRawPCIDevices(PVM pVM,
GuestPCIAddress.fromLong(guest);
Assert(GuestPCIAddress.valid());
- hrc = BusMgr->assignHostPCIDevice("pciraw", pInst, HostPCIAddress, GuestPCIAddress, true);
+ hrc = pBusMgr->assignHostPCIDevice("pciraw", pInst, HostPCIAddress, GuestPCIAddress, true);
if (hrc != S_OK)
return hrc;
@@ -638,13 +772,14 @@ void Console::attachStatusDriver(PCFGMNODE pCtlInst, PPDMLED *papLeds,
* in the emulation thread (EMT). Any per thread COM/XPCOM initialization
* is done here.
*
- * @param pVM VM handle.
+ * @param pUVM The user mode VM handle.
+ * @param pVM The cross context VM handle.
* @param pvConsole Pointer to the VMPowerUpTask object.
* @return VBox status code.
*
* @note Locks the Console object for writing.
*/
-DECLCALLBACK(int) Console::configConstructor(PVM pVM, void *pvConsole)
+DECLCALLBACK(int) Console::configConstructor(PUVM pUVM, PVM pVM, void *pvConsole)
{
LogFlowFuncEnter();
@@ -661,12 +796,12 @@ DECLCALLBACK(int) Console::configConstructor(PVM pVM, void *pvConsole)
* Set the VM handle and do the rest of the job in an worker method so we
* can easily reset the VM handle on failure.
*/
- PUVM pUVM = pConsole->mpUVM = VMR3GetUVM(pVM);
+ pConsole->mpUVM = pUVM;
VMR3RetainUVM(pUVM);
int vrc;
try
{
- vrc = pConsole->configConstructorInner(pVM, &alock);
+ vrc = pConsole->configConstructorInner(pUVM, pVM, &alock);
}
catch (...)
{
@@ -686,20 +821,20 @@ DECLCALLBACK(int) Console::configConstructor(PVM pVM, void *pvConsole)
* Worker for configConstructor.
*
* @return VBox status code.
- * @param pVM The VM handle.
+ * @param pUVM The user mode VM handle.
+ * @param pVM The cross context VM handle.
* @param pAlock The automatic lock instance. This is for when we have
* to leave it in order to avoid deadlocks (ext packs and
* more).
*/
-int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
+int Console::configConstructorInner(PUVM pUVM, PVM pVM, AutoWriteLock *pAlock)
{
- VMMDev *pVMMDev = m_pVMMDev;
- Assert(pVMMDev);
-
+ VMMDev *pVMMDev = m_pVMMDev; Assert(pVMMDev);
ComPtr<IMachine> pMachine = machine();
int rc;
HRESULT hrc;
+ Utf8Str strTmp;
Bstr bstr;
#define H() AssertMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_GENERAL_FAILURE)
@@ -746,7 +881,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
uMcfgBase = _4G - cbRamHole;
}
- BusAssignmentManager* BusMgr = mBusMgr = BusAssignmentManager::createInstance(chipsetType);
+ BusAssignmentManager *pBusMgr = mBusMgr = BusAssignmentManager::createInstance(chipsetType);
ULONG cCpus = 1;
hrc = pMachine->COMGETTER(CPUCount)(&cCpus); H();
@@ -756,6 +891,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
Bstr osTypeId;
hrc = pMachine->COMGETTER(OSTypeId)(osTypeId.asOutParam()); H();
+ LogRel(("Guest OS type: '%s'\n", Utf8Str(osTypeId).c_str()));
BOOL fIOAPIC;
hrc = biosSettings->COMGETTER(IOAPICEnabled)(&fIOAPIC); H();
@@ -773,7 +909,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
* Get root node first.
* This is the only node in the tree.
*/
- PCFGMNODE pRoot = CFGMR3GetRoot(pVM);
+ PCFGMNODE pRoot = CFGMR3GetRootU(pUVM);
Assert(pRoot);
// InsertConfigString throws
@@ -798,6 +934,15 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigInteger(pRoot, "PATMEnabled", 1); /* boolean */
InsertConfigInteger(pRoot, "CSAMEnabled", 1); /* boolean */
#endif
+
+#ifdef VBOX_WITH_RAW_RING1
+ if (osTypeId == "QNX")
+ {
+ /* QNX needs special treatment in raw mode due to its use of ring-1. */
+ InsertConfigInteger(pRoot, "RawR1Enabled", 1); /* boolean */
+ }
+#endif
+
/* Not necessary, but to make sure these two settings end up in the release log. */
BOOL fPageFusion = FALSE;
hrc = pMachine->COMGETTER(PageFusionEnabled)(&fPageFusion); H();
@@ -844,6 +989,14 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigInteger(pCPUM, "NT4LeafLimit", true);
}
+ /* Expose CMPXCHG16B. Currently a hack. */
+ if ( osTypeId == "Windows81_64"
+ || osTypeId == "Windows2012_64")
+ {
+ LogRel(("Enabling CMPXCHG16B for Windows 8.1 / 2k12 guests\n"));
+ InsertConfigInteger(pCPUM, "CMPXCHG16B", true);
+ }
+
/* Expose extended MWAIT features to Mac OS X guests. */
if (fOsXGuest)
{
@@ -851,128 +1004,188 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigInteger(pCPUM, "MWaitExtensions", true);
}
+ if (fOsXGuest)
+ {
+ InsertConfigInteger(pCPUM, "EnableHVP", 1);
+
+ /* Fake the CPU family/model so the guest works. This is partly
+ because older mac releases really doesn't work on newer cpus,
+ and partly because mac os x expects more from systems with newer
+ cpus (MSRs, power features, whatever). */
+ uint32_t uMaxIntelFamilyModelStep = UINT32_MAX;
+ if ( osTypeId == "MacOS"
+ || osTypeId == "MacOS_64")
+ uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 7); /* Penryn / X5482. */
+ else if ( osTypeId == "MacOS106"
+ || osTypeId == "MacOS106_64")
+ uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 7); /* Penryn / X5482 */
+ else if ( osTypeId == "MacOS107"
+ || osTypeId == "MacOS107_64")
+ uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 7); /* Penryn / X5482 */ /** @todo figure out what is required here. */
+ else if ( osTypeId == "MacOS108"
+ || osTypeId == "MacOS108_64")
+ uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 7); /* Penryn / X5482 */ /** @todo figure out what is required here. */
+ else if ( osTypeId == "MacOS109"
+ || osTypeId == "MacOS109_64")
+ uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 7); /* Penryn / X5482 */ /** @todo figure out what is required here. */
+ if (uMaxIntelFamilyModelStep != UINT32_MAX)
+ InsertConfigInteger(pCPUM, "MaxIntelFamilyModelStep", uMaxIntelFamilyModelStep);
+ }
+
+
+ /* Synthetic CPU */
+ BOOL fSyntheticCpu = false;
+ hrc = pMachine->GetCPUProperty(CPUPropertyType_Synthetic, &fSyntheticCpu); H();
+ InsertConfigInteger(pCPUM, "SyntheticCpu", fSyntheticCpu);
+
+ /* Physical Address Extension (PAE) */
+ BOOL fEnablePAE = false;
+ hrc = pMachine->GetCPUProperty(CPUPropertyType_PAE, &fEnablePAE); H();
+ InsertConfigInteger(pRoot, "EnablePAE", fEnablePAE);
+
+
/*
* Hardware virtualization extensions.
*/
- BOOL fHWVirtExEnabled;
- BOOL fHwVirtExtForced = false;
+ BOOL fSupportsHwVirtEx;
+ hrc = host->GetProcessorFeature(ProcessorFeature_HWVirtEx, &fSupportsHwVirtEx); H();
+
+ BOOL fIsGuest64Bit;
+ hrc = pMachine->GetCPUProperty(CPUPropertyType_LongMode, &fIsGuest64Bit); H();
+ if (fIsGuest64Bit)
+ {
+ BOOL fSupportsLongMode;
+ hrc = host->GetProcessorFeature(ProcessorFeature_LongMode, &fSupportsLongMode); H();
+ if (!fSupportsLongMode)
+ {
+ LogRel(("WARNING! 64-bit guest type selected but the host CPU does NOT support 64-bit.\n"));
+ fIsGuest64Bit = FALSE;
+ }
+ if (!fSupportsHwVirtEx)
+ {
+ LogRel(("WARNING! 64-bit guest type selected but the host CPU does NOT support HW virtualization.\n"));
+ fIsGuest64Bit = FALSE;
+ }
+ }
+
+ BOOL fHMEnabled;
+ hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_Enabled, &fHMEnabled); H();
+ if (cCpus > 1 && !fHMEnabled)
+ {
+ LogRel(("Forced fHMEnabled to TRUE by SMP guest.\n"));
+ fHMEnabled = TRUE;
+ }
+
+ BOOL fHMForced;
#ifdef VBOX_WITH_RAW_MODE
- hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_Enabled, &fHWVirtExEnabled); H();
- if (cCpus > 1) /** @todo SMP: This isn't nice, but things won't work on mac otherwise. */
- fHWVirtExEnabled = TRUE;
-# ifdef RT_OS_DARWIN
- fHwVirtExtForced = fHWVirtExEnabled;
-# else
/* - With more than 4GB PGM will use different RAMRANGE sizes for raw
mode and hv mode to optimize lookup times.
- - With more than one virtual CPU, raw-mode isn't a fallback option. */
- fHwVirtExtForced = fHWVirtExEnabled
- && ( cbRam + cbRamHole > _4G
- || cCpus > 1);
+ - With more than one virtual CPU, raw-mode isn't a fallback option.
+ - With a 64-bit guest, raw-mode isn't a fallback option either. */
+ fHMForced = fHMEnabled
+ && ( cbRam + cbRamHole > _4G
+ || cCpus > 1
+ || fIsGuest64Bit);
+# ifdef RT_OS_DARWIN
+ fHMForced = fHMEnabled;
# endif
+ if (fHMForced)
+ {
+ if (cbRam + cbRamHole > _4G)
+ LogRel(("fHMForced=true - Lots of RAM\n"));
+ if (cCpus > 1)
+ LogRel(("fHMForced=true - SMP\n"));
+ if (fIsGuest64Bit)
+ LogRel(("fHMForced=true - 64-bit guest\n"));
+# ifdef RT_OS_DARWIN
+ LogRel(("fHMForced=true - Darwin host\n"));
+# endif
+ }
#else /* !VBOX_WITH_RAW_MODE */
- fHWVirtExEnabled = fHwVirtExtForced = true;
+ fHMEnabled = fHMForced = TRUE;
+ LogRel(("fHMForced=true - No raw-mode support in this build!\n"));
#endif /* !VBOX_WITH_RAW_MODE */
- /* only honor the property value if there was no other reason to enable it */
- if (!fHwVirtExtForced)
+ if (!fHMForced) /* No need to query if already forced above. */
{
- hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_Force, &fHwVirtExtForced); H();
+ hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_Force, &fHMForced); H();
+ if (fHMForced)
+ LogRel(("fHMForced=true - HWVirtExPropertyType_Force\n"));
}
- InsertConfigInteger(pRoot, "HwVirtExtForced", fHwVirtExtForced);
-
-
- /*
- * MM values.
- */
- PCFGMNODE pMM;
- InsertConfigNode(pRoot, "MM", &pMM);
- InsertConfigInteger(pMM, "CanUseLargerHeap", chipsetType == ChipsetType_ICH9);
-
- /*
- * Hardware virtualization settings.
- */
- BOOL fIsGuest64Bit = false;
- PCFGMNODE pHWVirtExt;
- InsertConfigNode(pRoot, "HWVirtExt", &pHWVirtExt);
- if (fHWVirtExEnabled)
+ InsertConfigInteger(pRoot, "HMEnabled", fHMEnabled);
+
+ /* /EM/xzy */
+ PCFGMNODE pEM;
+ InsertConfigNode(pRoot, "EM", &pEM);
+
+ /* Triple fault behavior. */
+ BOOL fTripleFaultReset = false;
+ hrc = pMachine->GetCPUProperty(CPUPropertyType_TripleFaultReset, &fTripleFaultReset); H();
+ InsertConfigInteger(pEM, "TripleFaultReset", fTripleFaultReset);
+
+ /* /HM/xzy */
+ PCFGMNODE pHM;
+ InsertConfigNode(pRoot, "HM", &pHM);
+ InsertConfigInteger(pHM, "HMForced", fHMForced);
+ if (fHMEnabled)
{
- InsertConfigInteger(pHWVirtExt, "Enabled", 1);
-
/* Indicate whether 64-bit guests are supported or not. */
- /** @todo This is currently only forced off on 32-bit hosts only because it
- * makes a lof of difference there (REM and Solaris performance).
- */
- BOOL fSupportsLongMode = false;
- hrc = host->GetProcessorFeature(ProcessorFeature_LongMode,
- &fSupportsLongMode); H();
- hrc = guestOSType->COMGETTER(Is64Bit)(&fIsGuest64Bit); H();
-
- if (fSupportsLongMode && fIsGuest64Bit)
- {
- InsertConfigInteger(pHWVirtExt, "64bitEnabled", 1);
+ InsertConfigInteger(pHM, "64bitEnabled", fIsGuest64Bit);
#if ARCH_BITS == 32 /* The recompiler must use VBoxREM64 (32-bit host only). */
- PCFGMNODE pREM;
- InsertConfigNode(pRoot, "REM", &pREM);
- InsertConfigInteger(pREM, "64bitEnabled", 1);
-#endif
- }
-#if ARCH_BITS == 32 /* 32-bit guests only. */
- else
- {
- InsertConfigInteger(pHWVirtExt, "64bitEnabled", 0);
- }
+ PCFGMNODE pREM;
+ InsertConfigNode(pRoot, "REM", &pREM);
+ InsertConfigInteger(pREM, "64bitEnabled", 1);
#endif
/** @todo Not exactly pretty to check strings; VBOXOSTYPE would be better, but that requires quite a bit of API change in Main. */
- if ( !fIsGuest64Bit
- && fIOAPIC
+ if ( fIOAPIC
&& ( osTypeId == "WindowsNT4"
- || osTypeId == "Windows2000"
- || osTypeId == "WindowsXP"
- || osTypeId == "Windows2003"))
+ || osTypeId == "Windows2000"
+ || osTypeId == "WindowsXP"
+ || osTypeId == "Windows2003"))
{
/* Only allow TPR patching for NT, Win2k, XP and Windows Server 2003. (32 bits mode)
* We may want to consider adding more guest OSes (Solaris) later on.
*/
- InsertConfigInteger(pHWVirtExt, "TPRPatchingEnabled", 1);
+ InsertConfigInteger(pHM, "TPRPatchingEnabled", 1);
}
}
/* HWVirtEx exclusive mode */
- BOOL fHWVirtExExclusive = true;
- hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_Exclusive, &fHWVirtExExclusive); H();
- InsertConfigInteger(pHWVirtExt, "Exclusive", fHWVirtExExclusive);
+ BOOL fHMExclusive = true;
+ hrc = systemProperties->COMGETTER(ExclusiveHwVirt)(&fHMExclusive); H();
+ InsertConfigInteger(pHM, "Exclusive", fHMExclusive);
/* Nested paging (VT-x/AMD-V) */
BOOL fEnableNestedPaging = false;
hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_NestedPaging, &fEnableNestedPaging); H();
- InsertConfigInteger(pHWVirtExt, "EnableNestedPaging", fEnableNestedPaging);
+ InsertConfigInteger(pHM, "EnableNestedPaging", fEnableNestedPaging);
/* Large pages; requires nested paging */
BOOL fEnableLargePages = false;
hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_LargePages, &fEnableLargePages); H();
- InsertConfigInteger(pHWVirtExt, "EnableLargePages", fEnableLargePages);
+ InsertConfigInteger(pHM, "EnableLargePages", fEnableLargePages);
/* VPID (VT-x) */
BOOL fEnableVPID = false;
hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_VPID, &fEnableVPID); H();
- InsertConfigInteger(pHWVirtExt, "EnableVPID", fEnableVPID);
+ InsertConfigInteger(pHM, "EnableVPID", fEnableVPID);
- /* Physical Address Extension (PAE) */
- BOOL fEnablePAE = false;
- hrc = pMachine->GetCPUProperty(CPUPropertyType_PAE, &fEnablePAE); H();
- InsertConfigInteger(pRoot, "EnablePAE", fEnablePAE);
+ /* Unrestricted execution aka UX (VT-x) */
+ BOOL fEnableUX = false;
+ hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_UnrestrictedExecution, &fEnableUX); H();
+ InsertConfigInteger(pHM, "EnableUX", fEnableUX);
- /* Synthetic CPU */
- BOOL fSyntheticCpu = false;
- hrc = pMachine->GetCPUProperty(CPUPropertyType_Synthetic, &fSyntheticCpu); H();
- InsertConfigInteger(pCPUM, "SyntheticCpu", fSyntheticCpu);
+ /* Reset overwrite. */
+ if (isResetTurnedIntoPowerOff())
+ InsertConfigInteger(pRoot, "PowerOffInsteadOfReset", 1);
- if (fOsXGuest)
- InsertConfigInteger(pCPUM, "EnableHVP", 1);
- BOOL fPXEDebug;
- hrc = biosSettings->COMGETTER(PXEDebugEnabled)(&fPXEDebug); H();
+ /*
+ * MM values.
+ */
+ PCFGMNODE pMM;
+ InsertConfigNode(pRoot, "MM", &pMM);
+ InsertConfigInteger(pMM, "CanUseLargerHeap", chipsetType == ChipsetType_ICH9);
/*
* PDM config.
@@ -1039,7 +1252,11 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
hrc = bwGroups[i]->COMGETTER(Name)(strName.asOutParam()); H();
hrc = bwGroups[i]->COMGETTER(Type)(&enmType); H();
- hrc = bwGroups[i]->COMGETTER(MaxBytesPerSec)(&cMaxBytesPerSec); H();
+ hrc = bwGroups[i]->COMGETTER(MaxBytesPerSec)(&cMaxBytesPerSec); H();
+
+ if (strName.isEmpty())
+ return VMR3SetError(pUVM, VERR_CFGM_NO_NODE, RT_SRC_POS,
+ N_("No bandwidth group name specified"));
if (enmType == BandwidthGroupType_Disk)
{
@@ -1072,6 +1289,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
PCFGMNODE pLunL2 = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/Config/ */
PCFGMNODE pBiosCfg = NULL; /* /Devices/pcbios/0/Config/ */
PCFGMNODE pNetBootCfg = NULL; /* /Devices/pcbios/0/Config/NetBoot/ */
+ bool fHaveBiosScsiConfig = false;
InsertConfigNode(pRoot, "Devices", &pDevices);
@@ -1133,15 +1351,15 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigNode(pDevices, "ich9pcibridge", &pDev);
InsertConfigNode(pDev, "0", &pInst);
InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
- hrc = BusMgr->assignPCIDevice("ich9pcibridge", pInst); H();
+ hrc = pBusMgr->assignPCIDevice("ich9pcibridge", pInst); H();
InsertConfigNode(pDev, "1", &pInst);
InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
- hrc = BusMgr->assignPCIDevice("ich9pcibridge", pInst); H();
+ hrc = pBusMgr->assignPCIDevice("ich9pcibridge", pInst); H();
#ifdef VBOX_WITH_PCI_PASSTHROUGH
/* Add PCI passthrough devices */
- hrc = attachRawPCIDevices(pVM, BusMgr, pDevices); H();
+ hrc = attachRawPCIDevices(pUVM, pBusMgr, pDevices); H();
#endif
}
@@ -1181,11 +1399,11 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigNode(pInst, "Config", &pCfg);
bool fGetKeyFromRealSMC;
- Bstr bstrKey;
- rc = getSmcDeviceKey(pMachine, bstrKey.asOutParam(), &fGetKeyFromRealSMC);
+ Utf8Str strKey;
+ rc = getSmcDeviceKey(virtualBox, pMachine, &strKey, &fGetKeyFromRealSMC);
AssertRCReturn(rc, rc);
- InsertConfigString(pCfg, "DeviceKey", bstrKey);
+ InsertConfigString(pCfg, "DeviceKey", strKey);
InsertConfigInteger(pCfg, "GetKeyFromRealSMC", fGetKeyFromRealSMC);
}
@@ -1199,7 +1417,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
{
InsertConfigNode(pDevices, "lpc", &pDev);
InsertConfigNode(pDev, "0", &pInst);
- hrc = BusMgr->assignPCIDevice("lpc", pInst); H();
+ hrc = pBusMgr->assignPCIDevice("lpc", pInst); H();
InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
}
@@ -1225,6 +1443,9 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
Keyboard *pKeyboard = mKeyboard;
InsertConfigInteger(pCfg, "Object", (uintptr_t)pKeyboard);
+ Mouse *pMouse = mMouse;
+ PointingHIDType_T aPointingHID;
+ hrc = pMachine->COMGETTER(PointingHIDType)(&aPointingHID); H();
InsertConfigNode(pInst, "LUN#1", &pLunL0);
InsertConfigString(pLunL0, "Driver", "MouseQueue");
InsertConfigNode(pLunL0, "Config", &pCfg);
@@ -1233,7 +1454,6 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
InsertConfigString(pLunL1, "Driver", "MainMouse");
InsertConfigNode(pLunL1, "Config", &pCfg);
- Mouse *pMouse = mMouse;
InsertConfigInteger(pCfg, "Object", (uintptr_t)pMouse);
/*
@@ -1291,87 +1511,26 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
/*
* VGA.
*/
- InsertConfigNode(pDevices, "vga", &pDev);
- InsertConfigNode(pDev, "0", &pInst);
- InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
-
- hrc = BusMgr->assignPCIDevice("vga", pInst); H();
- InsertConfigNode(pInst, "Config", &pCfg);
- ULONG cVRamMBs;
- hrc = pMachine->COMGETTER(VRAMSize)(&cVRamMBs); H();
- InsertConfigInteger(pCfg, "VRamSize", cVRamMBs * _1M);
- ULONG cMonitorCount;
- hrc = pMachine->COMGETTER(MonitorCount)(&cMonitorCount); H();
- InsertConfigInteger(pCfg, "MonitorCount", cMonitorCount);
-#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
- InsertConfigInteger(pCfg, "R0Enabled", fHWVirtExEnabled);
-#endif
-
- /*
- * BIOS logo
- */
- BOOL fFadeIn;
- hrc = biosSettings->COMGETTER(LogoFadeIn)(&fFadeIn); H();
- InsertConfigInteger(pCfg, "FadeIn", fFadeIn ? 1 : 0);
- BOOL fFadeOut;
- hrc = biosSettings->COMGETTER(LogoFadeOut)(&fFadeOut); H();
- InsertConfigInteger(pCfg, "FadeOut", fFadeOut ? 1: 0);
- ULONG logoDisplayTime;
- hrc = biosSettings->COMGETTER(LogoDisplayTime)(&logoDisplayTime); H();
- InsertConfigInteger(pCfg, "LogoTime", logoDisplayTime);
- Bstr logoImagePath;
- hrc = biosSettings->COMGETTER(LogoImagePath)(logoImagePath.asOutParam()); H();
- InsertConfigString(pCfg, "LogoFile", Utf8Str(!logoImagePath.isEmpty() ? logoImagePath : "") );
-
- /*
- * Boot menu
- */
- BIOSBootMenuMode_T eBootMenuMode;
- int iShowBootMenu;
- biosSettings->COMGETTER(BootMenuMode)(&eBootMenuMode);
- switch (eBootMenuMode)
- {
- case BIOSBootMenuMode_Disabled: iShowBootMenu = 0; break;
- case BIOSBootMenuMode_MenuOnly: iShowBootMenu = 1; break;
- default: iShowBootMenu = 2; break;
- }
- InsertConfigInteger(pCfg, "ShowBootMenu", iShowBootMenu);
-
- /* Custom VESA mode list */
- unsigned cModes = 0;
- for (unsigned iMode = 1; iMode <= 16; ++iMode)
+ GraphicsControllerType_T graphicsController;
+ hrc = pMachine->COMGETTER(GraphicsControllerType)(&graphicsController); H();
+ switch (graphicsController)
{
- char szExtraDataKey[sizeof("CustomVideoModeXX")];
- RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%u", iMode);
- hrc = pMachine->GetExtraData(Bstr(szExtraDataKey).raw(), bstr.asOutParam()); H();
- if (bstr.isEmpty())
+ case GraphicsControllerType_Null:
break;
- InsertConfigString(pCfg, szExtraDataKey, bstr);
- ++cModes;
- }
- InsertConfigInteger(pCfg, "CustomVideoModes", cModes);
-
- /* VESA height reduction */
- ULONG ulHeightReduction;
- IFramebuffer *pFramebuffer = getDisplay()->getFramebuffer();
- if (pFramebuffer)
- {
- hrc = pFramebuffer->COMGETTER(HeightReduction)(&ulHeightReduction); H();
- }
- else
- {
- /* If framebuffer is not available, there is no height reduction. */
- ulHeightReduction = 0;
+ case GraphicsControllerType_VBoxVGA:
+#ifdef VBOX_WITH_VMSVGA
+ case GraphicsControllerType_VMSVGA:
+#endif
+ rc = configGraphicsController(pDevices, graphicsController, pBusMgr, pMachine, biosSettings,
+ RT_BOOL(fHMEnabled));
+ if (FAILED(rc))
+ return rc;
+ break;
+ default:
+ AssertMsgFailed(("Invalid graphicsController=%d\n", graphicsController));
+ return VMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Invalid graphics controller type '%d'"), graphicsController);
}
- InsertConfigInteger(pCfg, "HeightReduction", ulHeightReduction);
-
- /* Attach the display. */
- InsertConfigNode(pInst, "LUN#0", &pLunL0);
- InsertConfigString(pLunL0, "Driver", "MainDisplay");
- InsertConfigNode(pLunL0, "Config", &pCfg);
- Display *pDisplay = mDisplay;
- InsertConfigInteger(pCfg, "Object", (uintptr_t)pDisplay);
-
/*
* Firmware.
@@ -1399,6 +1558,8 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigString(pBiosCfg, "HardDiskDevice", "piix3ide");
InsertConfigString(pBiosCfg, "FloppyDevice", "i82078");
InsertConfigInteger(pBiosCfg, "IOAPIC", fIOAPIC);
+ BOOL fPXEDebug;
+ hrc = biosSettings->COMGETTER(PXEDebugEnabled)(&fPXEDebug); H();
InsertConfigInteger(pBiosCfg, "PXEDebug", fPXEDebug);
InsertConfigBytes(pBiosCfg, "UUID", &HardwareUuid,sizeof(HardwareUuid));
InsertConfigNode(pBiosCfg, "NetBoot", &pNetBootCfg);
@@ -1406,12 +1567,8 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigInteger(pBiosCfg, "McfgLength", cbMcfgLength);
DeviceType_T bootDevice;
- if (SchemaDefs::MaxBootPosition > 9)
- {
- AssertMsgFailed(("Too many boot devices %d\n",
- SchemaDefs::MaxBootPosition));
- return VERR_INVALID_PARAMETER;
- }
+ AssertMsgReturn(SchemaDefs::MaxBootPosition <= 9, ("Too many boot devices %d\n", SchemaDefs::MaxBootPosition),
+ VERR_INVALID_PARAMETER);
for (ULONG pos = 1; pos <= SchemaDefs::MaxBootPosition; ++pos)
{
@@ -1440,11 +1597,16 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
break;
default:
AssertMsgFailed(("Invalid bootDevice=%d\n", bootDevice));
- return VMSetError(pVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
- N_("Invalid boot device '%d'"), bootDevice);
+ return VMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Invalid boot device '%d'"), bootDevice);
}
InsertConfigString(pBiosCfg, szParamName, pszBootDevice);
}
+
+ /** @todo @bugref{7145}: We might want to enable this by default for new VMs. For now,
+ * this is required for Windows 2012 guests. */
+ if (osTypeId == "Windows2012_64")
+ InsertConfigInteger(pBiosCfg, "DmiExposeMemoryTable", 1); /* boolean */
}
else
{
@@ -1462,29 +1624,29 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
AssertRCReturn(rc, rc);
/* Get boot args */
- Bstr bootArgs;
- hrc = pMachine->GetExtraData(Bstr("VBoxInternal2/EfiBootArgs").raw(), bootArgs.asOutParam()); H();
+ Utf8Str bootArgs;
+ GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiBootArgs", &bootArgs);
/* Get device props */
- Bstr deviceProps;
- hrc = pMachine->GetExtraData(Bstr("VBoxInternal2/EfiDeviceProps").raw(), deviceProps.asOutParam()); H();
+ Utf8Str deviceProps;
+ GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiDeviceProps", &deviceProps);
/* Get GOP mode settings */
uint32_t u32GopMode = UINT32_MAX;
- hrc = pMachine->GetExtraData(Bstr("VBoxInternal2/EfiGopMode").raw(), bstr.asOutParam()); H();
- if (!bstr.isEmpty())
- u32GopMode = Utf8Str(bstr).toUInt32();
+ GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiGopMode", &strTmp);
+ if (!strTmp.isEmpty())
+ u32GopMode = strTmp.toUInt32();
/* UGA mode settings */
uint32_t u32UgaHorisontal = 0;
- hrc = pMachine->GetExtraData(Bstr("VBoxInternal2/EfiUgaHorizontalResolution").raw(), bstr.asOutParam()); H();
- if (!bstr.isEmpty())
- u32UgaHorisontal = Utf8Str(bstr).toUInt32();
+ GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiUgaHorizontalResolution", &strTmp);
+ if (!strTmp.isEmpty())
+ u32UgaHorisontal = strTmp.toUInt32();
uint32_t u32UgaVertical = 0;
- hrc = pMachine->GetExtraData(Bstr("VBoxInternal2/EfiUgaVerticalResolution").raw(), bstr.asOutParam()); H();
- if (!bstr.isEmpty())
- u32UgaVertical = Utf8Str(bstr).toUInt32();
+ GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiUgaVerticalResolution", &strTmp);
+ if (!strTmp.isEmpty())
+ u32UgaVertical = strTmp.toUInt32();
/*
* EFI subtree.
@@ -1512,11 +1674,10 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigInteger(pCfg, "DmiUseHostInfo", 1);
InsertConfigInteger(pCfg, "DmiExposeMemoryTable", 1);
}
- PCFGMNODE pDrv;
InsertConfigNode(pInst, "LUN#0", &pLunL0);
InsertConfigString(pLunL0, "Driver", "NvramStorage");
InsertConfigNode(pLunL0, "Config", &pCfg);
- InsertConfigInteger(pCfg, "Object", (uint64_t)mNvram);
+ InsertConfigInteger(pCfg, "Object", (uintptr_t)mNvram);
#ifdef DEBUG_vvl
InsertConfigInteger(pCfg, "PermanentSave", 1);
#endif
@@ -1570,14 +1731,32 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigInteger(pCtlInst, "Trusted", 1);
InsertConfigNode(pCtlInst, "Config", &pCfg);
+ static const char * const apszBiosConfigScsi[MAX_BIOS_LUN_COUNT] =
+ { "ScsiLUN1", "ScsiLUN2", "ScsiLUN3", "ScsiLUN4" };
+
+ static const char * const apszBiosConfigSata[MAX_BIOS_LUN_COUNT] =
+ { "SataLUN1", "SataLUN2", "SataLUN3", "SataLUN4" };
+
switch (enmCtrlType)
{
case StorageControllerType_LsiLogic:
{
- hrc = BusMgr->assignPCIDevice("lsilogic", pCtlInst); H();
+ hrc = pBusMgr->assignPCIDevice("lsilogic", pCtlInst); H();
InsertConfigInteger(pCfg, "Bootable", fBootable);
+ /* BIOS configuration values, first controller only. */
+ if (!pBusMgr->hasPCIDevice("lsilogicscsi", 1) && pBiosCfg)
+ {
+ if (!fHaveBiosScsiConfig)
+ {
+ fHaveBiosScsiConfig = true;
+ InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "lsilogicscsi");
+
+ hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H();
+ }
+ }
+
/* Attach the status driver */
Assert(cLedScsi >= 16);
attachStatusDriver(pCtlInst, &mapStorageLeds[iLedScsi], 0, 15,
@@ -1588,10 +1767,22 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
case StorageControllerType_BusLogic:
{
- hrc = BusMgr->assignPCIDevice("buslogic", pCtlInst); H();
+ hrc = pBusMgr->assignPCIDevice("buslogic", pCtlInst); H();
InsertConfigInteger(pCfg, "Bootable", fBootable);
+ /* BIOS configuration values, first controller only. */
+ if (!pBusMgr->hasPCIDevice("buslogic", 1) && pBiosCfg)
+ {
+ if (!fHaveBiosScsiConfig)
+ {
+ fHaveBiosScsiConfig = true;
+ InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "buslogic");
+
+ hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H();
+ }
+ }
+
/* Attach the status driver */
Assert(cLedScsi >= 16);
attachStatusDriver(pCtlInst, &mapStorageLeds[iLedScsi], 0, 15,
@@ -1602,7 +1793,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
case StorageControllerType_IntelAhci:
{
- hrc = BusMgr->assignPCIDevice("ahci", pCtlInst); H();
+ hrc = pBusMgr->assignPCIDevice("ahci", pCtlInst); H();
ULONG cPorts = 0;
hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H();
@@ -1610,12 +1801,14 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigInteger(pCfg, "Bootable", fBootable);
/* Needed configuration values for the bios, only first controller. */
- if (!BusMgr->hasPCIDevice("ahci", 1))
+ if (!pBusMgr->hasPCIDevice("ahci", 1))
{
if (pBiosCfg)
{
InsertConfigString(pBiosCfg, "SataHardDiskDevice", "ahci");
}
+
+ hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigSata); H();
}
/* Attach the status driver */
@@ -1633,7 +1826,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
/*
* IDE (update this when the main interface changes)
*/
- hrc = BusMgr->assignPCIDevice("piix3ide", pCtlInst); H();
+ hrc = pBusMgr->assignPCIDevice("piix3ide", pCtlInst); H();
InsertConfigString(pCfg, "Type", controllerString(enmCtrlType));
/* Attach the status driver */
Assert(cLedIde >= 4);
@@ -1669,11 +1862,27 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
case StorageControllerType_LsiLogicSas:
{
- hrc = BusMgr->assignPCIDevice("lsilogicsas", pCtlInst); H();
+ hrc = pBusMgr->assignPCIDevice("lsilogicsas", pCtlInst); H();
InsertConfigString(pCfg, "ControllerType", "SAS1068");
InsertConfigInteger(pCfg, "Bootable", fBootable);
+ /* BIOS configuration values, first controller only. */
+ if (!pBusMgr->hasPCIDevice("lsilogicsas", 1) && pBiosCfg)
+ {
+ if (!fHaveBiosScsiConfig)
+ {
+ fHaveBiosScsiConfig = true;
+ InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "lsilogicsas");
+
+ hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H();
+ }
+ }
+
+ ULONG cPorts = 0;
+ hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H();
+ InsertConfigInteger(pCfg, "NumPorts", cPorts);
+
/* Attach the status driver */
Assert(cLedSas >= 8);
attachStatusDriver(pCtlInst, &mapStorageLeds[iLedSas], 0, 7,
@@ -1714,8 +1923,9 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
false /* fAttachDetach */,
false /* fForceUnmount */,
false /* fHotplug */,
- pVM,
- paLedDevType);
+ pUVM,
+ paLedDevType,
+ NULL /* ppLunL0 */);
if (RT_FAILURE(rc))
return rc;
}
@@ -1778,9 +1988,9 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
default:
AssertMsgFailed(("Invalid network adapter type '%d' for slot '%d'",
adapterType, ulInstance));
- return VMSetError(pVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
- N_("Invalid network adapter type '%d' for slot '%d'"),
- adapterType, ulInstance);
+ return VMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Invalid network adapter type '%d' for slot '%d'"),
+ adapterType, ulInstance);
}
InsertConfigNode(pDev, Utf8StrFmt("%u", ulInstance).c_str(), &pInst);
@@ -1807,7 +2017,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
#ifdef VMWARE_NET_IN_SLOT_11
/*
* Dirty hack for PCI slot compatibility with VMWare,
- * it assigns slot 11 to the first network controller.
+ * it assigns slot 0x11 to the first network controller.
*/
if (iPCIDeviceNo == 3 && adapterType == NetworkAdapterType_I82545EM)
{
@@ -1818,7 +2028,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
iPCIDeviceNo = 3;
#endif
PCIBusAddress PCIAddr = PCIBusAddress(0, iPCIDeviceNo, 0);
- hrc = BusMgr->assignPCIDevice(pszAdapterName, pInst, PCIAddr); H();
+ hrc = pBusMgr->assignPCIDevice(pszAdapterName, pInst, PCIAddr); H();
InsertConfigNode(pInst, "Config", &pCfg);
#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE /* not safe here yet. */ /** @todo Make PCNet ring-0 safe on 32-bit mac kernels! */
@@ -1872,7 +2082,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
char *macStr = (char*)macAddrUtf8.c_str();
Assert(strlen(macStr) == 12);
RTMAC Mac;
- memset(&Mac, 0, sizeof(Mac));
+ RT_ZERO(Mac);
char *pMac = (char*)&Mac;
for (uint32_t i = 0; i < 6; ++i)
{
@@ -2051,7 +2261,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigNode(pDev, "0", &pInst);
InsertConfigNode(pInst, "Config", &pCfg);
InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
- hrc = BusMgr->assignPCIDevice("VMMDev", pInst); H();
+ hrc = pBusMgr->assignPCIDevice("VMMDev", pInst); H();
Bstr hwVersion;
hrc = pMachine->COMGETTER(HardwareVersion)(hwVersion.asOutParam()); H();
@@ -2108,7 +2318,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigNode(pDevices, "ichac97", &pDev);
InsertConfigNode(pDev, "0", &pInst);
InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
- hrc = BusMgr->assignPCIDevice("ichac97", pInst); H();
+ hrc = pBusMgr->assignPCIDevice("ichac97", pInst); H();
InsertConfigNode(pInst, "Config", &pCfg);
break;
}
@@ -2132,7 +2342,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigNode(pDevices, "hda", &pDev);
InsertConfigNode(pDev, "0", &pInst);
InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
- hrc = BusMgr->assignPCIDevice("hda", pInst); H();
+ hrc = pBusMgr->assignPCIDevice("hda", pInst); H();
InsertConfigNode(pInst, "Config", &pCfg);
}
}
@@ -2217,34 +2427,55 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
}
/*
- * The USB Controller.
+ * The USB Controllers.
*/
- ComPtr<IUSBController> USBCtlPtr;
- hrc = pMachine->COMGETTER(USBController)(USBCtlPtr.asOutParam());
- if (USBCtlPtr)
+ com::SafeIfaceArray<IUSBController> usbCtrls;
+ hrc = pMachine->COMGETTER(USBControllers)(ComSafeArrayAsOutParam(usbCtrls)); H();
+ bool fOhciPresent = false; /**< Flag whether at least one OHCI controller is presnet. */
+
+ for (size_t i = 0; i < usbCtrls.size(); ++i)
{
- BOOL fOhciEnabled;
- hrc = USBCtlPtr->COMGETTER(Enabled)(&fOhciEnabled); H();
- if (fOhciEnabled)
+ USBControllerType_T enmCtrlType;
+ rc = usbCtrls[i]->COMGETTER(Type)(&enmCtrlType); H();
+ if (enmCtrlType == USBControllerType_OHCI)
{
- InsertConfigNode(pDevices, "usb-ohci", &pDev);
- InsertConfigNode(pDev, "0", &pInst);
- InsertConfigNode(pInst, "Config", &pCfg);
- InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
- hrc = BusMgr->assignPCIDevice("usb-ohci", pInst); H();
- InsertConfigNode(pInst, "LUN#0", &pLunL0);
- InsertConfigString(pLunL0, "Driver", "VUSBRootHub");
- InsertConfigNode(pLunL0, "Config", &pCfg);
+ fOhciPresent = true;
+ break;
+ }
+ }
- /*
- * Attach the status driver.
- */
- attachStatusDriver(pInst, &mapUSBLed[0], 0, 0, NULL, NULL, 0);
+ /*
+ * Currently EHCI is only enabled when a OHCI controller is present too.
+ * This might change when XHCI is supported.
+ */
+ if (fOhciPresent)
+ mfVMHasUsbController = true;
+
+ if (mfVMHasUsbController)
+ {
+ for (size_t i = 0; i < usbCtrls.size(); ++i)
+ {
+ USBControllerType_T enmCtrlType;
+ rc = usbCtrls[i]->COMGETTER(Type)(&enmCtrlType); H();
+
+ if (enmCtrlType == USBControllerType_OHCI)
+ {
+ InsertConfigNode(pDevices, "usb-ohci", &pDev);
+ InsertConfigNode(pDev, "0", &pInst);
+ InsertConfigNode(pInst, "Config", &pCfg);
+ InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
+ hrc = pBusMgr->assignPCIDevice("usb-ohci", pInst); H();
+ InsertConfigNode(pInst, "LUN#0", &pLunL0);
+ InsertConfigString(pLunL0, "Driver", "VUSBRootHub");
+ InsertConfigNode(pLunL0, "Config", &pCfg);
+ /*
+ * Attach the status driver.
+ */
+ attachStatusDriver(pInst, &mapUSBLed[0], 0, 0, NULL, NULL, 0);
+ }
#ifdef VBOX_WITH_EHCI
- BOOL fEHCIEnabled;
- hrc = USBCtlPtr->COMGETTER(EnabledEHCI)(&fEHCIEnabled); H();
- if (fEHCIEnabled)
+ else if (enmCtrlType == USBControllerType_EHCI)
{
/*
* USB 2.0 is only available if the proper ExtPack is installed.
@@ -2263,7 +2494,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
InsertConfigNode(pDev, "0", &pInst);
InsertConfigNode(pInst, "Config", &pCfg);
InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
- hrc = BusMgr->assignPCIDevice("usb-ehci", pInst); H();
+ hrc = pBusMgr->assignPCIDevice("usb-ehci", pInst); H();
InsertConfigNode(pInst, "LUN#0", &pLunL0);
InsertConfigString(pLunL0, "Driver", "VUSBRootHub");
@@ -2279,7 +2510,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
{
/* Always fatal! Up to VBox 4.0.4 we allowed to start the VM anyway
* but this induced problems when the user saved + restored the VM! */
- return VMSetError(pVM, VERR_NOT_FOUND, RT_SRC_POS,
+ return VMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
N_("Implementation of the USB 2.0 controller not found!\n"
"Because the USB 2.0 controller state is part of the saved "
"VM state, the VM cannot be started. To fix "
@@ -2290,142 +2521,134 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
# endif
}
#endif
+ } /* for every USB controller. */
- /*
- * Virtual USB Devices.
- */
- PCFGMNODE pUsbDevices = NULL;
- InsertConfigNode(pRoot, "USB", &pUsbDevices);
+
+ /*
+ * Virtual USB Devices.
+ */
+ PCFGMNODE pUsbDevices = NULL;
+ InsertConfigNode(pRoot, "USB", &pUsbDevices);
#ifdef VBOX_WITH_USB
- {
- /*
- * Global USB options, currently unused as we'll apply the 2.0 -> 1.1 morphing
- * on a per device level now.
- */
- InsertConfigNode(pUsbDevices, "USBProxy", &pCfg);
- InsertConfigNode(pCfg, "GlobalConfig", &pCfg);
- // This globally enables the 2.0 -> 1.1 device morphing of proxied devices to keep windows quiet.
- //InsertConfigInteger(pCfg, "Force11Device", true);
- // The following breaks stuff, but it makes MSDs work in vista. (I include it here so
- // that it's documented somewhere.) Users needing it can use:
- // VBoxManage setextradata "myvm" "VBoxInternal/USB/USBProxy/GlobalConfig/Force11PacketSize" 1
- //InsertConfigInteger(pCfg, "Force11PacketSize", true);
- }
+ {
+ /*
+ * Global USB options, currently unused as we'll apply the 2.0 -> 1.1 morphing
+ * on a per device level now.
+ */
+ InsertConfigNode(pUsbDevices, "USBProxy", &pCfg);
+ InsertConfigNode(pCfg, "GlobalConfig", &pCfg);
+ // This globally enables the 2.0 -> 1.1 device morphing of proxied devices to keep windows quiet.
+ //InsertConfigInteger(pCfg, "Force11Device", true);
+ // The following breaks stuff, but it makes MSDs work in vista. (I include it here so
+ // that it's documented somewhere.) Users needing it can use:
+ // VBoxManage setextradata "myvm" "VBoxInternal/USB/USBProxy/GlobalConfig/Force11PacketSize" 1
+ //InsertConfigInteger(pCfg, "Force11PacketSize", true);
+ }
#endif
-#ifdef VBOX_WITH_USB_VIDEO
-
- InsertConfigNode(pUsbDevices, "Webcam", &pDev);
+#ifdef VBOX_WITH_USB_CARDREADER
+ BOOL aEmulatedUSBCardReaderEnabled = FALSE;
+ hrc = pMachine->COMGETTER(EmulatedUSBCardReaderEnabled)(&aEmulatedUSBCardReaderEnabled); H();
+ if (aEmulatedUSBCardReaderEnabled)
+ {
+ InsertConfigNode(pUsbDevices, "CardReader", &pDev);
InsertConfigNode(pDev, "0", &pInst);
InsertConfigNode(pInst, "Config", &pCfg);
-# if 0 /* Experiments with attaching */
- InsertConfigInteger(pCfg, "USBVER", RT_BIT(2));
-# endif
- InsertConfigNode(pInst, "LUN#0", &pLunL0);
-# ifdef VBOX_WITH_USB_VIDEO_TEST
- InsertConfigString(pLunL0, "Driver", "WebcamFileFeeder");
- InsertConfigNode(pLunL0, "Config", &pCfg);
- InsertConfigString(pCfg, "DirToFeed", "out");
-# else
- InsertConfigString(pLunL0, "Driver", "UsbWebcamInterface");
- InsertConfigNode(pLunL0, "Config", &pCfg);
- InsertConfigInteger(pCfg, "Object", mUsbWebcamInterface);
-# endif
-#endif
-#ifdef VBOX_WITH_USB_CARDREADER
- BOOL aEmulatedUSBCardReaderEnabled = FALSE;
- hrc = pMachine->COMGETTER(EmulatedUSBCardReaderEnabled)(&aEmulatedUSBCardReaderEnabled); H();
- if (aEmulatedUSBCardReaderEnabled)
- {
- InsertConfigNode(pUsbDevices, "CardReader", &pDev);
- InsertConfigNode(pDev, "0", &pInst);
- InsertConfigNode(pInst, "Config", &pCfg);
- InsertConfigNode(pInst, "LUN#0", &pLunL0);
+ InsertConfigNode(pInst, "LUN#0", &pLunL0);
# ifdef VBOX_WITH_USB_CARDREADER_TEST
- InsertConfigString(pLunL0, "Driver", "DrvDirectCardReader");
- InsertConfigNode(pLunL0, "Config", &pCfg);
+ InsertConfigString(pLunL0, "Driver", "DrvDirectCardReader");
+ InsertConfigNode(pLunL0, "Config", &pCfg);
# else
- InsertConfigString(pLunL0, "Driver", "UsbCardReader");
- InsertConfigNode(pLunL0, "Config", &pCfg);
- InsertConfigInteger(pCfg, "Object", (uintptr_t)mUsbCardReader);
+ InsertConfigString(pLunL0, "Driver", "UsbCardReader");
+ InsertConfigNode(pLunL0, "Config", &pCfg);
+ InsertConfigInteger(pCfg, "Object", (uintptr_t)mUsbCardReader);
# endif
- }
+ }
#endif
# if 0 /* Virtual MSD*/
+ InsertConfigNode(pUsbDevices, "Msd", &pDev);
+ InsertConfigNode(pDev, "0", &pInst);
+ InsertConfigNode(pInst, "Config", &pCfg);
+ InsertConfigNode(pInst, "LUN#0", &pLunL0);
+
+ InsertConfigString(pLunL0, "Driver", "SCSI");
+ InsertConfigNode(pLunL0, "Config", &pCfg);
+
+ InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
+ InsertConfigString(pLunL1, "Driver", "Block");
+ InsertConfigNode(pLunL1, "Config", &pCfg);
+ InsertConfigString(pCfg, "Type", "HardDisk");
+ InsertConfigInteger(pCfg, "Mountable", 0);
+
+ InsertConfigNode(pLunL1, "AttachedDriver", &pLunL2);
+ InsertConfigString(pLunL2, "Driver", "VD");
+ InsertConfigNode(pLunL2, "Config", &pCfg);
+ InsertConfigString(pCfg, "Path", "/Volumes/DataHFS/bird/VDIs/linux.vdi");
+ InsertConfigString(pCfg, "Format", "VDI");
+# endif
- InsertConfigNode(pUsbDevices, "Msd", &pDev);
+ /* Virtual USB Mouse/Tablet */
+ if ( aPointingHID == PointingHIDType_USBMouse
+ || aPointingHID == PointingHIDType_USBTablet
+ || aPointingHID == PointingHIDType_USBMultiTouch)
+ {
+ InsertConfigNode(pUsbDevices, "HidMouse", &pDev);
InsertConfigNode(pDev, "0", &pInst);
InsertConfigNode(pInst, "Config", &pCfg);
- InsertConfigNode(pInst, "LUN#0", &pLunL0);
- InsertConfigString(pLunL0, "Driver", "SCSI");
+ if (aPointingHID == PointingHIDType_USBMouse)
+ InsertConfigString(pCfg, "Mode", "relative");
+ else
+ InsertConfigString(pCfg, "Mode", "absolute");
+ InsertConfigNode(pInst, "LUN#0", &pLunL0);
+ InsertConfigString(pLunL0, "Driver", "MouseQueue");
InsertConfigNode(pLunL0, "Config", &pCfg);
+ InsertConfigInteger(pCfg, "QueueSize", 128);
InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
- InsertConfigString(pLunL1, "Driver", "Block");
+ InsertConfigString(pLunL1, "Driver", "MainMouse");
InsertConfigNode(pLunL1, "Config", &pCfg);
- InsertConfigString(pCfg, "Type", "HardDisk");
- InsertConfigInteger(pCfg, "Mountable", 0);
-
- InsertConfigNode(pLunL1, "AttachedDriver", &pLunL2);
- InsertConfigString(pLunL2, "Driver", "VD");
- InsertConfigNode(pLunL2, "Config", &pCfg);
- InsertConfigString(pCfg, "Path", "/Volumes/DataHFS/bird/VDIs/linux.vdi");
- InsertConfigString(pCfg, "Format", "VDI");
-# endif
-
- /* Virtual USB Mouse/Tablet */
- PointingHIDType_T aPointingHID;
- hrc = pMachine->COMGETTER(PointingHIDType)(&aPointingHID); H();
- if (aPointingHID == PointingHIDType_USBMouse || aPointingHID == PointingHIDType_USBTablet)
- {
- InsertConfigNode(pUsbDevices, "HidMouse", &pDev);
- InsertConfigNode(pDev, "0", &pInst);
- InsertConfigNode(pInst, "Config", &pCfg);
+ InsertConfigInteger(pCfg, "Object", (uintptr_t)pMouse);
+ }
+ if (aPointingHID == PointingHIDType_USBMultiTouch)
+ {
+ InsertConfigNode(pDev, "1", &pInst);
+ InsertConfigNode(pInst, "Config", &pCfg);
- if (aPointingHID == PointingHIDType_USBTablet)
- {
- InsertConfigInteger(pCfg, "Absolute", 1);
- }
- else
- {
- InsertConfigInteger(pCfg, "Absolute", 0);
- }
- InsertConfigNode(pInst, "LUN#0", &pLunL0);
- InsertConfigString(pLunL0, "Driver", "MouseQueue");
- InsertConfigNode(pLunL0, "Config", &pCfg);
- InsertConfigInteger(pCfg, "QueueSize", 128);
+ InsertConfigString(pCfg, "Mode", "multitouch");
+ InsertConfigNode(pInst, "LUN#0", &pLunL0);
+ InsertConfigString(pLunL0, "Driver", "MouseQueue");
+ InsertConfigNode(pLunL0, "Config", &pCfg);
+ InsertConfigInteger(pCfg, "QueueSize", 128);
- InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
- InsertConfigString(pLunL1, "Driver", "MainMouse");
- InsertConfigNode(pLunL1, "Config", &pCfg);
- pMouse = mMouse;
- InsertConfigInteger(pCfg, "Object", (uintptr_t)pMouse);
- }
+ InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
+ InsertConfigString(pLunL1, "Driver", "MainMouse");
+ InsertConfigNode(pLunL1, "Config", &pCfg);
+ InsertConfigInteger(pCfg, "Object", (uintptr_t)pMouse);
+ }
- /* Virtual USB Keyboard */
- KeyboardHIDType_T aKbdHID;
- hrc = pMachine->COMGETTER(KeyboardHIDType)(&aKbdHID); H();
- if (aKbdHID == KeyboardHIDType_USBKeyboard)
- {
- InsertConfigNode(pUsbDevices, "HidKeyboard", &pDev);
- InsertConfigNode(pDev, "0", &pInst);
- InsertConfigNode(pInst, "Config", &pCfg);
+ /* Virtual USB Keyboard */
+ KeyboardHIDType_T aKbdHID;
+ hrc = pMachine->COMGETTER(KeyboardHIDType)(&aKbdHID); H();
+ if (aKbdHID == KeyboardHIDType_USBKeyboard)
+ {
+ InsertConfigNode(pUsbDevices, "HidKeyboard", &pDev);
+ InsertConfigNode(pDev, "0", &pInst);
+ InsertConfigNode(pInst, "Config", &pCfg);
- InsertConfigNode(pInst, "LUN#0", &pLunL0);
- InsertConfigString(pLunL0, "Driver", "KeyboardQueue");
- InsertConfigNode(pLunL0, "Config", &pCfg);
- InsertConfigInteger(pCfg, "QueueSize", 64);
+ InsertConfigNode(pInst, "LUN#0", &pLunL0);
+ InsertConfigString(pLunL0, "Driver", "KeyboardQueue");
+ InsertConfigNode(pLunL0, "Config", &pCfg);
+ InsertConfigInteger(pCfg, "QueueSize", 64);
- InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
- InsertConfigString(pLunL1, "Driver", "MainKeyboard");
- InsertConfigNode(pLunL1, "Config", &pCfg);
- pKeyboard = mKeyboard;
- InsertConfigInteger(pCfg, "Object", (uintptr_t)pKeyboard);
- }
+ InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
+ InsertConfigString(pLunL1, "Driver", "MainKeyboard");
+ InsertConfigNode(pLunL1, "Config", &pCfg);
+ pKeyboard = mKeyboard;
+ InsertConfigInteger(pCfg, "Object", (uintptr_t)pKeyboard);
}
}
@@ -2530,7 +2753,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
{
BOOL fSupports3D = VBoxOglIs3DAccelerationSupported();
if (!fSupports3D)
- return VMSetError(pVM, VERR_NOT_AVAILABLE, RT_SRC_POS,
+ return VMR3SetError(pUVM, VERR_NOT_AVAILABLE, RT_SRC_POS,
N_("This VM was configured to use 3D acceleration. However, the "
"3D support of the host is not working properly and the "
"VM cannot be started. To fix this problem, either "
@@ -2576,7 +2799,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
* Guest property service
*/
- rc = configGuestProperties(this, pVM);
+ rc = configGuestProperties(this, pUVM);
#endif /* VBOX_WITH_GUEST_PROPS defined */
#ifdef VBOX_WITH_GUEST_CONTROL
@@ -2594,22 +2817,22 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
hrc = biosSettings->COMGETTER(ACPIEnabled)(&fACPI); H();
if (fACPI)
{
- BOOL fCpuHotPlug = false;
- BOOL fShowCpu = fOsXGuest;
/* Always show the CPU leafs when we have multiple VCPUs or when the IO-APIC is enabled.
* The Windows SMP kernel needs a CPU leaf or else its idle loop will burn cpu cycles; the
* intelppm driver refuses to register an idle state handler.
- */
- if ((cCpus > 1) || fIOAPIC)
+ * Always show CPU leafs for OS X guests. */
+ BOOL fShowCpu = fOsXGuest;
+ if (cCpus > 1 || fIOAPIC)
fShowCpu = true;
+ BOOL fCpuHotPlug;
hrc = pMachine->COMGETTER(CPUHotPlugEnabled)(&fCpuHotPlug); H();
InsertConfigNode(pDevices, "acpi", &pDev);
InsertConfigNode(pDev, "0", &pInst);
InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
InsertConfigNode(pInst, "Config", &pCfg);
- hrc = BusMgr->assignPCIDevice("acpi", pInst); H();
+ hrc = pBusMgr->assignPCIDevice("acpi", pInst); H();
InsertConfigInteger(pCfg, "RamSize", cbRam);
InsertConfigInteger(pCfg, "RamHoleSize", cbRamHole);
@@ -2629,7 +2852,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
if (fOsXGuest && fAudioEnabled)
{
PCIBusAddress Address;
- if (BusMgr->findPCIAddress("hda", 0, Address))
+ if (pBusMgr->findPCIAddress("hda", 0, Address))
{
uint32_t u32AudioPCIAddr = (Address.miDevice << 16) | Address.miFn;
InsertConfigInteger(pCfg, "AudioPciAddress", u32AudioPCIAddr);
@@ -2720,6 +2943,10 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
// InsertConfig threw something:
return x.m_vrc;
}
+ catch (HRESULT hrcXcpt)
+ {
+ AssertMsgFailedReturn(("hrc=%Rhrc\n", hrcXcpt), VERR_GENERAL_FAILURE);
+ }
#ifdef VBOX_WITH_EXTPACK
/*
@@ -2737,7 +2964,13 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
* Apply the CFGM overlay.
*/
if (RT_SUCCESS(rc))
- rc = configCfgmOverlay(pVM, virtualBox, pMachine);
+ rc = configCfgmOverlay(pRoot, virtualBox, pMachine);
+
+ /*
+ * Dump all extradata API settings tweaks, both global and per VM.
+ */
+ if (RT_SUCCESS(rc))
+ rc = configDumpAPISettingsTweaks(virtualBox, pMachine);
#undef H
@@ -2746,7 +2979,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
/*
* Register VM state change handler.
*/
- int rc2 = VMR3AtStateRegister(pVM, Console::vmstateChangeCallback, this);
+ int rc2 = VMR3AtStateRegister(pUVM, Console::vmstateChangeCallback, this);
AssertRC(rc2);
if (RT_SUCCESS(rc))
rc = rc2;
@@ -2754,7 +2987,7 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
/*
* Register VM runtime error handler.
*/
- rc2 = VMR3AtRuntimeErrorRegister(pVM, Console::setVMRuntimeErrorCallback, this);
+ rc2 = VMR3AtRuntimeErrorRegister(pUVM, Console::setVMRuntimeErrorCallback, this);
AssertRC(rc2);
if (RT_SUCCESS(rc))
rc = rc2;
@@ -2768,16 +3001,16 @@ int Console::configConstructorInner(PVM pVM, AutoWriteLock *pAlock)
}
/**
- * Applies the CFGM overlay as specified by /VBoxInternal/XXX extra data
+ * Applies the CFGM overlay as specified by VBoxInternal/XXX extra data
* values.
*
* @returns VBox status code.
- * @param pVM The VM handle.
+ * @param pRoot The root of the configuration tree.
* @param pVirtualBox Pointer to the IVirtualBox interface.
* @param pMachine Pointer to the IMachine interface.
*/
/* static */
-int Console::configCfgmOverlay(PVM pVM, IVirtualBox *pVirtualBox, IMachine *pMachine)
+int Console::configCfgmOverlay(PCFGMNODE pRoot, IVirtualBox *pVirtualBox, IMachine *pMachine)
{
/*
* CFGM overlay handling.
@@ -2792,8 +3025,7 @@ int Console::configCfgmOverlay(PVM pVM, IVirtualBox *pVirtualBox, IMachine *pMac
* We first perform a run on global extra data, then on the machine
* extra data to support global settings with local overrides.
*/
- PCFGMNODE pRoot = CFGMR3GetRoot(pVM);
- int rc = VINF_SUCCESS;
+ int rc = VINF_SUCCESS;
try
{
/** @todo add support for removing nodes and byte blobs. */
@@ -2809,7 +3041,7 @@ int Console::configCfgmOverlay(PVM pVM, IVirtualBox *pVirtualBox, IMachine *pMac
size_t cGlobalValues = aGlobalExtraDataKeys.size();
hrc = pMachine->GetExtraDataKeys(ComSafeArrayAsOutParam(aMachineExtraDataKeys));
- AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc));
+ AssertMsg(SUCCEEDED(hrc), ("Machine::GetExtraDataKeys failed with %Rhrc\n", hrc));
// build a combined list from global keys...
std::list<Utf8Str> llExtraDataKeys;
@@ -2845,7 +3077,7 @@ int Console::configCfgmOverlay(PVM pVM, IVirtualBox *pVirtualBox, IMachine *pMac
hrc = pMachine->GetExtraData(Bstr(strKey).raw(),
bstrExtraDataValue.asOutParam());
if (FAILED(hrc))
- LogRel(("Warning: Cannot get extra data key %s, rc = %Rrc\n", strKey.c_str(), hrc));
+ LogRel(("Warning: Cannot get extra data key %s, rc = %Rhrc\n", strKey.c_str(), hrc));
/*
* The key will be in the format "Node1/Node2/Value" or simply "Value".
@@ -2897,16 +3129,36 @@ int Console::configCfgmOverlay(PVM pVM, IVirtualBox *pVirtualBox, IMachine *pMac
uint64_t u64Value;
/* check for type prefix first. */
- if (!strncmp(strCFGMValueUtf8.c_str(), "string:", sizeof("string:") - 1))
+ if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("string:")))
InsertConfigString(pNode, pszCFGMValueName, strCFGMValueUtf8.c_str() + sizeof("string:") - 1);
- else if (!strncmp(strCFGMValueUtf8.c_str(), "integer:", sizeof("integer:") - 1))
+ else if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("integer:")))
{
rc = RTStrToUInt64Full(strCFGMValueUtf8.c_str() + sizeof("integer:") - 1, 0, &u64Value);
if (RT_SUCCESS(rc))
rc = CFGMR3InsertInteger(pNode, pszCFGMValueName, u64Value);
}
- else if (!strncmp(strCFGMValueUtf8.c_str(), "bytes:", sizeof("bytes:") - 1))
- rc = VERR_NOT_IMPLEMENTED;
+ else if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("bytes:")))
+ {
+ char const *pszBase64 = strCFGMValueUtf8.c_str() + sizeof("bytes:") - 1;
+ ssize_t cbValue = RTBase64DecodedSize(pszBase64, NULL);
+ if (cbValue > 0)
+ {
+ void *pvBytes = RTMemTmpAlloc(cbValue);
+ if (pvBytes)
+ {
+ rc = RTBase64Decode(pszBase64, pvBytes, cbValue, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ rc = CFGMR3InsertBytes(pNode, pszCFGMValueName, pvBytes, cbValue);
+ RTMemTmpFree(pvBytes);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ else if (cbValue == 0)
+ rc = CFGMR3InsertBytes(pNode, pszCFGMValueName, NULL, 0);
+ else
+ rc = VERR_INVALID_BASE64_ENCODING;
+ }
/* auto detect type. */
else if (RT_SUCCESS(RTStrToUInt64Full(strCFGMValueUtf8.c_str(), 0, &u64Value)))
rc = CFGMR3InsertInteger(pNode, pszCFGMValueName, u64Value);
@@ -2925,14 +3177,205 @@ int Console::configCfgmOverlay(PVM pVM, IVirtualBox *pVirtualBox, IMachine *pMac
}
/**
+ * Dumps the API settings tweaks as specified by VBoxInternal2/XXX extra data
+ * values.
+ *
+ * @returns VBox status code.
+ * @param pVirtualBox Pointer to the IVirtualBox interface.
+ * @param pMachine Pointer to the IMachine interface.
+ */
+/* static */
+int Console::configDumpAPISettingsTweaks(IVirtualBox *pVirtualBox, IMachine *pMachine)
+{
+ {
+ SafeArray<BSTR> aGlobalExtraDataKeys;
+ HRESULT hrc = pVirtualBox->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys));
+ AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc));
+ bool hasKey = false;
+ for (size_t i = 0; i < aGlobalExtraDataKeys.size(); i++)
+ {
+ Utf8Str strKey(aGlobalExtraDataKeys[i]);
+ if (!strKey.startsWith("VBoxInternal2/"))
+ continue;
+
+ Bstr bstrValue;
+ hrc = pVirtualBox->GetExtraData(Bstr(strKey).raw(),
+ bstrValue.asOutParam());
+ if (FAILED(hrc))
+ continue;
+ if (!hasKey)
+ LogRel(("Global extradata API settings:\n"));
+ LogRel((" %s=\"%ls\"\n", strKey.c_str(), bstrValue.raw()));
+ hasKey = true;
+ }
+ }
+
+ {
+ SafeArray<BSTR> aMachineExtraDataKeys;
+ HRESULT hrc = pMachine->GetExtraDataKeys(ComSafeArrayAsOutParam(aMachineExtraDataKeys));
+ AssertMsg(SUCCEEDED(hrc), ("Machine::GetExtraDataKeys failed with %Rhrc\n", hrc));
+ bool hasKey = false;
+ for (size_t i = 0; i < aMachineExtraDataKeys.size(); i++)
+ {
+ Utf8Str strKey(aMachineExtraDataKeys[i]);
+ if (!strKey.startsWith("VBoxInternal2/"))
+ continue;
+
+ Bstr bstrValue;
+ hrc = pMachine->GetExtraData(Bstr(strKey).raw(),
+ bstrValue.asOutParam());
+ if (FAILED(hrc))
+ continue;
+ if (!hasKey)
+ LogRel(("Per-VM extradata API settings:\n"));
+ LogRel((" %s=\"%ls\"\n", strKey.c_str(), bstrValue.raw()));
+ hasKey = true;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+int Console::configGraphicsController(PCFGMNODE pDevices,
+ const GraphicsControllerType_T graphicsController,
+ BusAssignmentManager *pBusMgr,
+ const ComPtr<IMachine> &pMachine,
+ const ComPtr<IBIOSSettings> &biosSettings,
+ bool fHMEnabled)
+{
+ // InsertConfig* throws
+ try
+ {
+ PCFGMNODE pDev, pInst, pCfg, pLunL0;
+ HRESULT hrc;
+ Bstr bstr;
+ const char *pcszDevice = "vga";
+
+#define H() AssertMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_GENERAL_FAILURE)
+ InsertConfigNode(pDevices, pcszDevice, &pDev);
+ InsertConfigNode(pDev, "0", &pInst);
+ InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
+
+ hrc = pBusMgr->assignPCIDevice(pcszDevice, pInst); H();
+ InsertConfigNode(pInst, "Config", &pCfg);
+ ULONG cVRamMBs;
+ hrc = pMachine->COMGETTER(VRAMSize)(&cVRamMBs); H();
+ InsertConfigInteger(pCfg, "VRamSize", cVRamMBs * _1M);
+ ULONG cMonitorCount;
+ hrc = pMachine->COMGETTER(MonitorCount)(&cMonitorCount); H();
+ InsertConfigInteger(pCfg, "MonitorCount", cMonitorCount);
+#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
+ InsertConfigInteger(pCfg, "R0Enabled", fHMEnabled);
+#else
+ NOREF(fHMEnabled);
+#endif
+
+#ifdef VBOX_WITH_VMSVGA
+ if (graphicsController == GraphicsControllerType_VMSVGA)
+ {
+ InsertConfigInteger(pCfg, "VMSVGAEnabled", true);
+#ifdef VBOX_WITH_VMSVGA3D
+ IFramebuffer *pFramebuffer = getDisplay()->getFramebuffer();
+ if (pFramebuffer)
+ {
+ LONG64 winId = 0;
+ /* @todo deal with multimonitor setup */
+ Assert(cMonitorCount == 1);
+ hrc = pFramebuffer->COMGETTER(WinId)(&winId);
+ InsertConfigInteger(pCfg, "HostWindowId", winId);
+ }
+ BOOL f3DEnabled;
+ pMachine->COMGETTER(Accelerate3DEnabled)(&f3DEnabled);
+ InsertConfigInteger(pCfg, "VMSVGA3dEnabled", f3DEnabled);
+#endif
+ }
+#endif
+
+ /* Custom VESA mode list */
+ unsigned cModes = 0;
+ for (unsigned iMode = 1; iMode <= 16; ++iMode)
+ {
+ char szExtraDataKey[sizeof("CustomVideoModeXX")];
+ RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%u", iMode);
+ hrc = pMachine->GetExtraData(Bstr(szExtraDataKey).raw(), bstr.asOutParam()); H();
+ if (bstr.isEmpty())
+ break;
+ InsertConfigString(pCfg, szExtraDataKey, bstr);
+ ++cModes;
+ }
+ InsertConfigInteger(pCfg, "CustomVideoModes", cModes);
+
+ /* VESA height reduction */
+ ULONG ulHeightReduction;
+ IFramebuffer *pFramebuffer = getDisplay()->getFramebuffer();
+ if (pFramebuffer)
+ {
+ hrc = pFramebuffer->COMGETTER(HeightReduction)(&ulHeightReduction); H();
+ }
+ else
+ {
+ /* If framebuffer is not available, there is no height reduction. */
+ ulHeightReduction = 0;
+ }
+ InsertConfigInteger(pCfg, "HeightReduction", ulHeightReduction);
+
+ /*
+ * BIOS logo
+ */
+ BOOL fFadeIn;
+ hrc = biosSettings->COMGETTER(LogoFadeIn)(&fFadeIn); H();
+ InsertConfigInteger(pCfg, "FadeIn", fFadeIn ? 1 : 0);
+ BOOL fFadeOut;
+ hrc = biosSettings->COMGETTER(LogoFadeOut)(&fFadeOut); H();
+ InsertConfigInteger(pCfg, "FadeOut", fFadeOut ? 1: 0);
+ ULONG logoDisplayTime;
+ hrc = biosSettings->COMGETTER(LogoDisplayTime)(&logoDisplayTime); H();
+ InsertConfigInteger(pCfg, "LogoTime", logoDisplayTime);
+ Bstr logoImagePath;
+ hrc = biosSettings->COMGETTER(LogoImagePath)(logoImagePath.asOutParam()); H();
+ InsertConfigString(pCfg, "LogoFile", Utf8Str(!logoImagePath.isEmpty() ? logoImagePath : "") );
+
+ /*
+ * Boot menu
+ */
+ BIOSBootMenuMode_T eBootMenuMode;
+ int iShowBootMenu;
+ biosSettings->COMGETTER(BootMenuMode)(&eBootMenuMode);
+ switch (eBootMenuMode)
+ {
+ case BIOSBootMenuMode_Disabled: iShowBootMenu = 0; break;
+ case BIOSBootMenuMode_MenuOnly: iShowBootMenu = 1; break;
+ default: iShowBootMenu = 2; break;
+ }
+ InsertConfigInteger(pCfg, "ShowBootMenu", iShowBootMenu);
+
+ /* Attach the display. */
+ InsertConfigNode(pInst, "LUN#0", &pLunL0);
+ InsertConfigString(pLunL0, "Driver", "MainDisplay");
+ InsertConfigNode(pLunL0, "Config", &pCfg);
+ Display *pDisplay = mDisplay;
+ InsertConfigInteger(pCfg, "Object", (uintptr_t)pDisplay);
+ }
+ catch (ConfigError &x)
+ {
+ // InsertConfig threw something:
+ return x.m_vrc;
+ }
+
+#undef H
+
+ return VINF_SUCCESS;
+}
+
+
+/**
* Ellipsis to va_list wrapper for calling setVMRuntimeErrorCallback.
*/
-/*static*/
-void Console::setVMRuntimeErrorCallbackF(PVM pVM, void *pvConsole, uint32_t fFlags, const char *pszErrorId, const char *pszFormat, ...)
+void Console::setVMRuntimeErrorCallbackF(uint32_t fFlags, const char *pszErrorId, const char *pszFormat, ...)
{
va_list va;
va_start(va, pszFormat);
- setVMRuntimeErrorCallback(pVM, pvConsole, fFlags, pszErrorId, pszFormat, va);
+ setVMRuntimeErrorCallback(NULL, this, fFlags, pszErrorId, pszFormat, va);
va_end(va);
}
@@ -2971,8 +3414,9 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
bool fAttachDetach,
bool fForceUnmount,
bool fHotplug,
- PVM pVM,
- DeviceType_T *paLedDevType)
+ PUVM pUVM,
+ DeviceType_T *paLedDevType,
+ PCFGMNODE *ppLunL0)
{
// InsertConfig* throws
try
@@ -3009,7 +3453,7 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
{
/* Unmount existing media only for floppy and DVD drives. */
PPDMIBASE pBase;
- rc = PDMR3QueryLun(pVM, pcszDevice, uInstance, uLUN, &pBase);
+ rc = PDMR3QueryLun(pUVM, pcszDevice, uInstance, uLUN, &pBase);
if (RT_FAILURE(rc))
{
if (rc == VERR_PDM_LUN_NOT_FOUND || rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
@@ -3031,7 +3475,7 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
}
}
- rc = PDMR3DeviceDetach(pVM, pcszDevice, uInstance, uLUN, fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG);
+ rc = PDMR3DeviceDetach(pUVM, pcszDevice, uInstance, uLUN, fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG);
if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
rc = VINF_SUCCESS;
AssertRCReturn(rc, rc);
@@ -3043,6 +3487,8 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
}
InsertConfigNode(pCtlInst, Utf8StrFmt("LUN#%u", uLUN).c_str(), &pLunL0);
+ if (ppLunL0)
+ *ppLunL0 = pLunL0;
PCFGMNODE pCfg = CFGMR3GetChild(pCtlInst, "Config");
if (pCfg)
@@ -3101,8 +3547,13 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
*/
ComPtr<IMediumFormat> pMediumFormat;
hrc = pMedium->COMGETTER(MediumFormat)(pMediumFormat.asOutParam()); H();
- ULONG uCaps;
- hrc = pMediumFormat->COMGETTER(Capabilities)(&uCaps); H();
+ ULONG uCaps = 0;
+ com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap;
+ hrc = pMediumFormat->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap)); H();
+
+ for (ULONG j = 0; j < mediumFormatCap.size(); j++)
+ uCaps |= mediumFormatCap[j];
+
if (uCaps & MediumFormatCapabilities_File)
{
Bstr strFile;
@@ -3135,8 +3586,7 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
{
const char *pszUnit;
uint64_t u64Print = formatDiskSize((uint64_t)i64Size, &pszUnit);
- setVMRuntimeErrorCallbackF(pVM, this, 0,
- "FatPartitionDetected",
+ setVMRuntimeErrorCallbackF(0, "FatPartitionDetected",
N_("The medium '%ls' has a logical size of %RU64%s "
"but the file system the medium is located on seems "
"to be FAT(32) which cannot handle files bigger than 4GB.\n"
@@ -3167,8 +3617,7 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
const char *pszUnitMax;
uint64_t u64PrintSiz = formatDiskSize((LONG64)i64Size, &pszUnitSiz);
uint64_t u64PrintMax = formatDiskSize(maxSize, &pszUnitMax);
- setVMRuntimeErrorCallbackF(pVM, this, 0,
- "FatPartitionDetected", /* <= not exact but ... */
+ setVMRuntimeErrorCallbackF(0, "FatPartitionDetected", /* <= not exact but ... */
N_("The medium '%ls' has a logical size of %RU64%s "
"but the file system the medium is located on can "
"only handle files up to %RU64%s in theory.\n"
@@ -3191,8 +3640,7 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
{
const char *pszUnit;
uint64_t u64Print = formatDiskSize(i64Size, &pszUnit);
- setVMRuntimeErrorCallbackF(pVM, this, 0,
- "FatPartitionDetected",
+ setVMRuntimeErrorCallbackF(0, "FatPartitionDetected",
#ifdef RT_OS_WINDOWS
N_("The snapshot folder of this VM '%ls' seems to be located on "
"a FAT(32) file system. The logical size of the medium '%ls' "
@@ -3233,8 +3681,7 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
if ( enmFsTypeFile == RTFSTYPE_EXT4
|| enmFsTypeFile == RTFSTYPE_XFS)
{
- setVMRuntimeErrorCallbackF(pVM, this, 0,
- "Ext4PartitionDetected",
+ setVMRuntimeErrorCallbackF(0, "Ext4PartitionDetected",
N_("The host I/O cache for at least one controller is disabled "
"and the medium '%ls' for this VM "
"is located on an %s partition. There is a known Linux "
@@ -3251,8 +3698,7 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
|| enmFsTypeSnap == RTFSTYPE_XFS)
&& !mfSnapshotFolderExt4WarningShown)
{
- setVMRuntimeErrorCallbackF(pVM, this, 0,
- "Ext4PartitionDetected",
+ setVMRuntimeErrorCallbackF(0, "Ext4PartitionDetected",
N_("The host I/O cache for at least one controller is disabled "
"and the snapshot folder for this VM "
"is located on an %s partition. There is a known Linux "
@@ -3271,19 +3717,17 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
}
}
- if ( pMedium
- && ( lType == DeviceType_DVD
- || lType == DeviceType_Floppy))
+ if (pMedium)
{
- /*
- * Informative logging.
- */
- ComPtr<IMediumFormat> pMediumFormat;
- hrc = pMedium->COMGETTER(MediumFormat)(pMediumFormat.asOutParam()); H();
- ULONG uCaps;
- hrc = pMediumFormat->COMGETTER(Capabilities)(&uCaps); H();
- if (uCaps & MediumFormatCapabilities_File)
+ BOOL fHostDrive;
+ hrc = pMedium->COMGETTER(HostDrive)(&fHostDrive); H();
+ if ( ( lType == DeviceType_DVD
+ || lType == DeviceType_Floppy)
+ && !fHostDrive)
{
+ /*
+ * Informative logging.
+ */
Bstr strFile;
hrc = pMedium->COMGETTER(Location)(strFile.asOutParam()); H();
Utf8Str utfFile = Utf8Str(strFile);
@@ -3326,8 +3770,8 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
if (fAttachDetach)
{
/* Attach the new driver. */
- rc = PDMR3DeviceAttach(pVM, pcszDevice, uInstance, uLUN,
- fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
+ rc = PDMR3DeviceAttach(pUVM, pcszDevice, uInstance, uLUN,
+ fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
AssertRCReturn(rc, rc);
/* There is no need to handle removable medium mounting, as we
@@ -3346,7 +3790,7 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
#undef H
- return VINF_SUCCESS;;
+ return VINF_SUCCESS;
}
int Console::configMedium(PCFGMNODE pLunL0,
@@ -3443,10 +3887,7 @@ int Console::configMedium(PCFGMNODE pLunL0,
{
Bstr loc;
hrc = pMedium->COMGETTER(Location)(loc.asOutParam()); H();
- setVMRuntimeErrorCallbackF(VMR3GetVM(mpUVM),
- this,
- 0,
- "DvdOrFloppyImageInaccessible",
+ setVMRuntimeErrorCallbackF(0, "DvdOrFloppyImageInaccessible",
"The image file '%ls' is inaccessible and is being ignored. Please select a different image file for the virtual %s drive.",
loc.raw(),
enmType == DeviceType_DVD ? "DVD" : "floppy");
@@ -3700,8 +4141,6 @@ int Console::configNetwork(const char *pszDevice,
*/
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- PVM pVM = VMR3GetVM(mpUVM); /* We're on an EMT, so this is safe. */
-
ComPtr<IMachine> pMachine = machine();
ComPtr<IVirtualBox> virtualBox;
@@ -3726,7 +4165,7 @@ int Console::configNetwork(const char *pszDevice,
if (fAttachDetach)
{
- rc = PDMR3DeviceDetach(pVM, pszDevice, uInstance, uLun, 0 /*fFlags*/);
+ rc = PDMR3DeviceDetach(mpUVM, pszDevice, uInstance, uLun, 0 /*fFlags*/);
if (rc == VINF_PDM_NO_DRIVER_ATTACHED_TO_LUN)
rc = VINF_SUCCESS;
AssertLogRelRCReturn(rc, rc);
@@ -3903,6 +4342,10 @@ int Console::configNetwork(const char *pszDevice,
if (!fValid)
continue;
+ if (strName.isEmpty())
+ VMSetError(VMR3GetVM(mpUVM), VERR_CFGM_NO_NODE, RT_SRC_POS,
+ N_("NAT redirection rule without a name"));
+
InsertConfigNode(pCfg, strName.c_str(), &pPF);
InsertConfigString(pPF, "Protocol", strProto);
@@ -3932,7 +4375,7 @@ int Console::configNetwork(const char *pszDevice,
switch (hrc)
{
case VERR_ACCESS_DENIED:
- return VMSetError(pVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
+ return VMSetError(VMR3GetVM(mpUVM), VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
"Failed to open '/dev/net/tun' for read/write access. Please check the "
"permissions of that node. Either run 'chmod 0666 /dev/net/tun' or "
"change the group of that node and make yourself a member of that group. Make "
@@ -3940,7 +4383,7 @@ int Console::configNetwork(const char *pszDevice,
"using udev"));
default:
AssertMsgFailed(("Could not attach to host interface! Bad!\n"));
- return VMSetError(pVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
+ return VMSetError(VMR3GetVM(mpUVM), VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
"Failed to initialize Host Interface Networking"));
}
}
@@ -3961,7 +4404,7 @@ int Console::configNetwork(const char *pszDevice,
hrc = aNetworkAdapter->COMGETTER(BridgedInterface)(BridgedIfName.asOutParam());
if (FAILED(hrc))
{
- LogRel(("NetworkAttachmentType_Bridged: COMGETTER(BridgedInterface) failed, hrc (0x%x)", hrc));
+ LogRel(("NetworkAttachmentType_Bridged: COMGETTER(BridgedInterface) failed, hrc (0x%x)\n", hrc));
H();
}
@@ -3983,7 +4426,7 @@ int Console::configNetwork(const char *pszDevice,
// * See @bugref{4750}.
// * hrc = aNetworkAdapter->Detach(); H();
// */
-// return VMSetError(pVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
+// return VMSetError(VMR3GetVM(mpUVM), VERR_INTERNAL_ERROR, RT_SRC_POS,
// N_("Malformed host interface networking name '%ls'"),
// BridgedIfName.raw());
// }
@@ -4019,7 +4462,7 @@ int Console::configNetwork(const char *pszDevice,
if (!SUCCEEDED(hrc))
{
AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: FindByName failed, rc=%Rhrc (0x%x)", hrc, hrc));
- return VMSetError(pVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
+ return VMSetError(VMR3GetVM(mpUVM), VERR_INTERNAL_ERROR, RT_SRC_POS,
N_("Nonexistent host networking interface, name '%ls'"),
BridgedIfName.raw());
}
@@ -4028,13 +4471,13 @@ int Console::configNetwork(const char *pszDevice,
hrc = hostInterface->COMGETTER(InterfaceType)(&eIfType);
if (FAILED(hrc))
{
- LogRel(("NetworkAttachmentType_Bridged: COMGETTER(InterfaceType) failed, hrc (0x%x)", hrc));
+ LogRel(("NetworkAttachmentType_Bridged: COMGETTER(InterfaceType) failed, hrc (0x%x)\n", hrc));
H();
}
if (eIfType != HostNetworkInterfaceType_Bridged)
{
- return VMSetError(pVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
+ return VMSetError(VMR3GetVM(mpUVM), VERR_INTERNAL_ERROR, RT_SRC_POS,
N_("Interface ('%ls') is not a Bridged Adapter interface"),
BridgedIfName.raw());
}
@@ -4042,7 +4485,7 @@ int Console::configNetwork(const char *pszDevice,
hrc = hostInterface->COMGETTER(Id)(bstr.asOutParam());
if (FAILED(hrc))
{
- LogRel(("NetworkAttachmentType_Bridged: COMGETTER(Id) failed, hrc (0x%x)", hrc));
+ LogRel(("NetworkAttachmentType_Bridged: COMGETTER(Id) failed, hrc (0x%x)\n", hrc));
H();
}
Guid hostIFGuid(bstr);
@@ -4064,7 +4507,7 @@ int Console::configNetwork(const char *pszDevice,
if (hrc != S_OK)
{
VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
- LogRel(("NetworkAttachmentType_Bridged: VBoxNetCfgWinGetComponentByGuid failed, hrc (0x%x)", hrc));
+ LogRel(("NetworkAttachmentType_Bridged: VBoxNetCfgWinGetComponentByGuid failed, hrc (0x%x)\n", hrc));
H();
}
#define VBOX_WIN_BINDNAME_PREFIX "\\DEVICE\\"
@@ -4124,14 +4567,14 @@ int Console::configNetwork(const char *pszDevice,
* This works and performs better than bridging a physical
* interface via the current FreeBSD vboxnetflt implementation.
*/
- if (!strncmp(pszBridgedIfName, "tap", sizeof "tap" - 1)) {
+ if (!strncmp(pszBridgedIfName, RT_STR_TUPLE("tap"))) {
hrc = attachToTapInterface(aNetworkAdapter);
if (FAILED(hrc))
{
switch (hrc)
{
case VERR_ACCESS_DENIED:
- return VMSetError(pVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
+ return VMSetError(VMR3GetVM(mpUVM), VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
"Failed to open '/dev/%s' for read/write access. Please check the "
"permissions of that node, and that the net.link.tap.user_open "
"sysctl is set. Either run 'chmod 0666 /dev/%s' or "
@@ -4139,7 +4582,7 @@ int Console::configNetwork(const char *pszDevice,
"a member of that group. Make sure that these changes are permanent."), pszBridgedIfName, pszBridgedIfName);
default:
AssertMsgFailed(("Could not attach to tap interface! Bad!\n"));
- return VMSetError(pVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
+ return VMSetError(VMR3GetVM(mpUVM), VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
"Failed to initialize Host Interface Networking"));
}
}
@@ -4164,12 +4607,12 @@ int Console::configNetwork(const char *pszDevice,
{
struct ifreq Req;
RT_ZERO(Req);
- strncpy(Req.ifr_name, pszBridgedIfName, sizeof(Req.ifr_name) - 1);
+ RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pszBridgedIfName);
if (ioctl(iSock, SIOCGIFFLAGS, &Req) >= 0)
if ((Req.ifr_flags & IFF_UP) == 0)
- setVMRuntimeErrorCallbackF(pVM, this, 0, "BridgedInterfaceDown",
- "Bridged interface %s is down. Guest will not be able to use this interface",
- pszBridgedIfName);
+ setVMRuntimeErrorCallbackF(0, "BridgedInterfaceDown",
+ N_("Bridged interface %s is down. Guest will not be able to use this interface"),
+ pszBridgedIfName);
close(iSock);
}
@@ -4213,7 +4656,7 @@ int Console::configNetwork(const char *pszDevice,
{
struct iwreq WRq;
- memset(&WRq, 0, sizeof(WRq));
+ RT_ZERO(WRq);
strncpy(WRq.ifr_name, pszBridgedIfName, IFNAMSIZ);
bool fSharedMacOnWire = ioctl(iSock, SIOCGIWNAME, &WRq) >= 0;
close(iSock);
@@ -4234,7 +4677,7 @@ int Console::configNetwork(const char *pszDevice,
struct ieee80211req WReq;
uint8_t abData[32];
- memset(&WReq, 0, sizeof(WReq));
+ RT_ZERO(WReq);
strncpy(WReq.i_name, pszBridgedIfName, sizeof(WReq.i_name));
WReq.i_type = IEEE80211_IOC_SSID;
WReq.i_val = -1;
@@ -4390,7 +4833,7 @@ int Console::configNetwork(const char *pszDevice,
if (!SUCCEEDED(rc))
{
LogRel(("NetworkAttachmentType_HostOnly: FindByName failed, rc (0x%x)\n", rc));
- return VMSetError(pVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
+ return VMSetError(VMR3GetVM(mpUVM), VERR_INTERNAL_ERROR, RT_SRC_POS,
N_("Nonexistent host networking interface, name '%ls'"),
HostOnlyName.raw());
}
@@ -4415,7 +4858,7 @@ int Console::configNetwork(const char *pszDevice,
}
if (eIfType != HostNetworkInterfaceType_HostOnly)
- return VMSetError(pVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
+ return VMSetError(VMR3GetVM(mpUVM), VERR_INTERNAL_ERROR, RT_SRC_POS,
N_("Interface ('%ls') is not a Host-Only Adapter interface"),
HostOnlyName.raw());
@@ -4591,6 +5034,23 @@ int Console::configNetwork(const char *pszDevice,
break;
}
+ case NetworkAttachmentType_NATNetwork:
+ {
+ hrc = aNetworkAdapter->COMGETTER(NATNetwork)(bstr.asOutParam()); H();
+ if (!bstr.isEmpty())
+ {
+ /** @todo add intnet prefix to separate namespaces, and add trunk if dealing with vboxnatX */
+ InsertConfigString(pLunL0, "Driver", "IntNet");
+ InsertConfigNode(pLunL0, "Config", &pCfg);
+ InsertConfigString(pCfg, "Network", bstr);
+ InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_WhateverNone);
+ InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
+ networkName = bstr;
+ trunkType = Bstr(TRUNKTYPE_WHATEVER);
+ }
+ break;
+ }
+
default:
AssertMsgFailed(("should not get here!\n"));
break;
@@ -4609,12 +5069,13 @@ int Console::configNetwork(const char *pszDevice,
case NetworkAttachmentType_HostOnly:
case NetworkAttachmentType_NAT:
case NetworkAttachmentType_Generic:
+ case NetworkAttachmentType_NATNetwork:
{
if (SUCCEEDED(hrc) && SUCCEEDED(rc))
{
if (fAttachDetach)
{
- rc = PDMR3DriverAttach(pVM, pszDevice, uInstance, uLun, 0 /*fFlags*/, NULL /* ppBase */);
+ rc = PDMR3DriverAttach(mpUVM, pszDevice, uInstance, uLun, 0 /*fFlags*/, NULL /* ppBase */);
//AssertRC(rc);
}
@@ -4627,7 +5088,11 @@ int Console::configNetwork(const char *pszDevice,
/* Stop the hostonly DHCP Server */
}
- if (!networkName.isEmpty())
+ /*
+ * NAT networks start their DHCP server theirself, see NATNetwork::Start()
+ */
+ if ( !networkName.isEmpty()
+ && eAttachmentType != NetworkAttachmentType_NATNetwork)
{
/*
* Until we implement service reference counters DHCP Server will be stopped
@@ -4643,7 +5108,7 @@ int Console::configNetwork(const char *pszDevice,
hrc = dhcpServer->COMGETTER(Enabled)(&fEnabledDhcp);
if (FAILED(hrc))
{
- LogRel(("DHCP svr: COMGETTER(Enabled) failed, hrc (%Rhrc)", hrc));
+ LogRel(("DHCP svr: COMGETTER(Enabled) failed, hrc (%Rhrc)\n", hrc));
H();
}
@@ -4724,13 +5189,13 @@ static void configSetProperty(VMMDev * const pVMMDev,
AssertPtrReturnVoid(pszFlags);
parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
parms[0].u.pointer.addr = (void *)pszName;
- parms[0].u.pointer.size = strlen(pszName) + 1;
+ parms[0].u.pointer.size = (uint32_t)strlen(pszName) + 1;
parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
parms[1].u.pointer.addr = (void *)pszValue;
- parms[1].u.pointer.size = strlen(pszValue) + 1;
+ parms[1].u.pointer.size = (uint32_t)strlen(pszValue) + 1;
parms[2].type = VBOX_HGCM_SVC_PARM_PTR;
parms[2].u.pointer.addr = (void *)pszFlags;
- parms[2].u.pointer.size = strlen(pszFlags) + 1;
+ parms[2].u.pointer.size = (uint32_t)strlen(pszFlags) + 1;
pVMMDev->hgcmHostCall("VBoxGuestPropSvc", guestProp::SET_PROP_HOST, 3,
&parms[0]);
}
@@ -4766,7 +5231,7 @@ int configSetGlobalPropertyFlags(VMMDev * const pVMMDev,
* Set up the Guest Property service, populate it with properties read from
* the machine XML and set a couple of initial properties.
*/
-/* static */ int Console::configGuestProperties(void *pvConsole, PVM pVM)
+/* static */ int Console::configGuestProperties(void *pvConsole, PUVM pUVM)
{
#ifdef VBOX_WITH_GUEST_PROPS
AssertReturn(pvConsole, VERR_GENERAL_FAILURE);
@@ -4798,7 +5263,7 @@ int configSetGlobalPropertyFlags(VMMDev * const pVMMDev,
{
PFNDBGFHANDLEREXT pfnHandler = (PFNDBGFHANDLEREXT)(uintptr_t)Params[0].u.pointer.addr;
void *pService = (void*)Params[1].u.pointer.addr;
- DBGFR3InfoRegisterExternal(pVM, "guestprops", "Display the guest properties", pfnHandler, pService);
+ DBGFR3InfoRegisterExternal(pUVM, "guestprops", "Display the guest properties", pfnHandler, pService);
}
}
@@ -4822,7 +5287,7 @@ int configSetGlobalPropertyFlags(VMMDev * const pVMMDev,
ComSafeArrayAsOutParam(valuesOut),
ComSafeArrayAsOutParam(timestampsOut),
ComSafeArrayAsOutParam(flagsOut));
- AssertMsgReturn(SUCCEEDED(hrc), ("hrc=%Rrc\n", hrc), VERR_GENERAL_FAILURE);
+ AssertMsgReturn(SUCCEEDED(hrc), ("hrc=%Rhrc\n", hrc), VERR_GENERAL_FAILURE);
size_t cProps = namesOut.size();
size_t cAlloc = cProps + 1;
if ( valuesOut.size() != cProps
diff --git a/src/VBox/Main/src-client/ConsoleImplTeleporter.cpp b/src/VBox/Main/src-client/ConsoleImplTeleporter.cpp
index 7e765352..e5666f3f 100644
--- a/src/VBox/Main/src-client/ConsoleImplTeleporter.cpp
+++ b/src/VBox/Main/src-client/ConsoleImplTeleporter.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2010-2012 Oracle Corporation
+ * Copyright (C) 2010-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;
@@ -239,7 +239,7 @@ Console::teleporterSrcReadACK(TeleporterStateSrc *pState, const char *pszWhich,
if (!strcmp(szMsg, "ACK"))
return S_OK;
- if (!strncmp(szMsg, "NACK=", sizeof("NACK=") - 1))
+ if (!strncmp(szMsg, RT_STR_TUPLE("NACK=")))
{
char *pszMsgText = strchr(szMsg, ';');
if (pszMsgText)
@@ -573,7 +573,7 @@ static SSMSTRMOPS const g_teleporterTcpOps =
static void teleporterProgressCancelCallback(void *pvUser)
{
TeleporterState *pState = (TeleporterState *)pvUser;
- SSMR3Cancel(VMR3GetVM(pState->mpUVM));
+ SSMR3Cancel(pState->mpUVM);
if (!pState->mfIsSource)
{
TeleporterStateTrg *pStateTrg = (TeleporterStateTrg *)pState;
@@ -584,7 +584,7 @@ static void teleporterProgressCancelCallback(void *pvUser)
/**
* @copydoc PFNVMPROGRESS
*/
-static DECLCALLBACK(int) teleporterProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
+static DECLCALLBACK(int) teleporterProgressCallback(PUVM pUVM, unsigned uPercent, void *pvUser)
{
TeleporterState *pState = (TeleporterState *)pvUser;
if (pState->mptrProgress)
@@ -597,12 +597,13 @@ static DECLCALLBACK(int) teleporterProgressCallback(PVM pVM, unsigned uPercent,
hrc = pState->mptrProgress->COMGETTER(Canceled)(&fCanceled);
if (SUCCEEDED(hrc) && fCanceled)
{
- SSMR3Cancel(VMR3GetVM(pState->mpUVM));
+ SSMR3Cancel(pState->mpUVM);
return VERR_SSM_CANCELLED;
}
}
}
+ NOREF(pUVM);
return VINF_SUCCESS;
}
@@ -685,7 +686,7 @@ Console::teleporterSrc(TeleporterStateSrc *pState)
RTSocketRetain(pState->mhSocket);
void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(pState));
- vrc = VMR3Teleport(VMR3GetVM(pState->mpUVM),
+ vrc = VMR3Teleport(pState->mpUVM,
pState->mcMsMaxDowntime,
&g_teleporterTcpOps, pvUser,
teleporterProgressCallback, pvUser,
@@ -810,7 +811,11 @@ Console::teleporterSrcThreadWrapper(RTTHREAD hThread, void *pvUser)
ptrVM.release();
pState->mptrConsole->mVMIsAlreadyPoweringOff = true; /* (Make sure we stick in the TeleportingPausedVM state.) */
+ autoLock.release();
+
hrc = pState->mptrConsole->powerDown();
+
+ autoLock.acquire();
pState->mptrConsole->mVMIsAlreadyPoweringOff = false;
pState->mptrProgress->notifyComplete(hrc);
@@ -886,7 +891,7 @@ Console::teleporterSrcThreadWrapper(RTTHREAD hThread, void *pvUser)
if (pState->mfSuspendedByUs)
{
autoLock.release();
- int rc = VMR3Resume(VMR3GetVM(pState->mpUVM));
+ int rc = VMR3Resume(pState->mpUVM, VMRESUMEREASON_TELEPORT_FAILED);
AssertLogRelMsgRC(rc, ("VMR3Resume -> %Rrc\n", rc));
autoLock.acquire();
}
@@ -932,7 +937,7 @@ Console::Teleport(IN_BSTR aHostname, ULONG aPort, IN_BSTR aPassword, ULONG aMaxD
*/
CheckComArgOutPointerValid(aProgress);
CheckComArgStrNotEmptyOrNull(aHostname);
- CheckComArgStrNotEmptyOrNull(aPassword);
+ CheckComArgNotNull(aPassword);
CheckComArgExprMsg(aPort, aPort > 0 && aPort <= 65535, ("is %u", aPort));
CheckComArgExprMsg(aMaxDowntime, aMaxDowntime > 0, ("is %u", aMaxDowntime));
@@ -1337,18 +1342,18 @@ Console::teleporterTrgServeConnection(RTSOCKET Sock, void *pvUser)
if (RT_FAILURE(vrc))
break;
- int vrc2 = VMR3AtErrorRegisterU(pState->mpUVM,
- Console::genericVMSetErrorCallback, &pState->mErrorText); AssertRC(vrc2);
+ int vrc2 = VMR3AtErrorRegister(pState->mpUVM,
+ Console::genericVMSetErrorCallback, &pState->mErrorText); AssertRC(vrc2);
RTSocketRetain(pState->mhSocket); /* For concurrent access by I/O thread and EMT. */
pState->moffStream = 0;
void *pvUser2 = static_cast<void *>(static_cast<TeleporterState *>(pState));
- vrc = VMR3LoadFromStream(VMR3GetVM(pState->mpUVM),
+ vrc = VMR3LoadFromStream(pState->mpUVM,
&g_teleporterTcpOps, pvUser2,
teleporterProgressCallback, pvUser2);
RTSocketRelease(pState->mhSocket);
- vrc2 = VMR3AtErrorDeregister(VMR3GetVM(pState->mpUVM), Console::genericVMSetErrorCallback, &pState->mErrorText); AssertRC(vrc2);
+ vrc2 = VMR3AtErrorDeregister(pState->mpUVM, Console::genericVMSetErrorCallback, &pState->mErrorText); AssertRC(vrc2);
if (RT_FAILURE(vrc))
{
@@ -1411,7 +1416,7 @@ Console::teleporterTrgServeConnection(RTSOCKET Sock, void *pvUser)
if (RT_SUCCESS(vrc))
{
if (!strcmp(szCmd, "hand-over-resume"))
- vrc = VMR3Resume(VMR3GetVM(pState->mpUVM));
+ vrc = VMR3Resume(pState->mpUVM, VMRESUMEREASON_TELEPORTED);
else
pState->mptrConsole->setMachineState(MachineState_Paused);
fDone = true;
diff --git a/src/VBox/Main/src-client/ConsoleVRDPServer.cpp b/src/VBox/Main/src-client/ConsoleVRDPServer.cpp
index f907b189..4247f2fb 100644
--- a/src/VBox/Main/src-client/ConsoleVRDPServer.cpp
+++ b/src/VBox/Main/src-client/ConsoleVRDPServer.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;
@@ -28,6 +28,7 @@
#ifdef VBOX_WITH_USB_CARDREADER
# include "UsbCardReader.h"
#endif
+#include "UsbWebcamInterface.h"
#include "Global.h"
#include "AutoCaller.h"
@@ -666,7 +667,6 @@ DECLCALLBACK(int) ConsoleVRDPServer::VRDPCallbackQueryProperty(void *pvCallback
*pcbOut = (uint32_t)cbPortRange;
} break;
-#ifdef VBOX_WITH_VRDP_VIDEO_CHANNEL
case VRDE_QP_VIDEO_CHANNEL:
{
com::Bstr bstr;
@@ -750,7 +750,6 @@ DECLCALLBACK(int) ConsoleVRDPServer::VRDPCallbackQueryProperty(void *pvCallback
*pcbOut = sizeof(uint32_t);
} break;
-#endif /* VBOX_WITH_VRDP_VIDEO_CHANNEL */
case VRDE_QP_FEATURE:
{
@@ -924,6 +923,16 @@ DECLCALLBACK(void) ConsoleVRDPServer::VRDPCallbackClientConnect(void *pvCallback
ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
server->mConsole->VRDPClientConnect(u32ClientId);
+
+ /* Should the server report usage of an interface for each client?
+ * Similar to Intercept.
+ */
+ int c = ASMAtomicIncS32(&server->mcClients);
+ if (c == 1)
+ {
+ /* Features which should be enabled only if there is a client. */
+ server->remote3DRedirect(true);
+ }
}
DECLCALLBACK(void) ConsoleVRDPServer::VRDPCallbackClientDisconnect(void *pvCallback, uint32_t u32ClientId, uint32_t fu32Intercepted)
@@ -947,6 +956,13 @@ DECLCALLBACK(void) ConsoleVRDPServer::VRDPCallbackClientDisconnect(void *pvCallb
AssertFailed();
}
}
+
+ int c = ASMAtomicDecS32(&server->mcClients);
+ if (c == 0)
+ {
+ /* Features which should be enabled only if there is a client. */
+ server->remote3DRedirect(false);
+ }
}
DECLCALLBACK(int) ConsoleVRDPServer::VRDPCallbackIntercept(void *pvCallback, uint32_t u32ClientId, uint32_t fu32Intercept, void **ppvIntercept)
@@ -1332,6 +1348,8 @@ ConsoleVRDPServer::ConsoleVRDPServer(Console *console)
mhServer = 0;
mServerInterfaceVersion = 0;
+ mcInResize = 0;
+
m_fGuestWantsAbsolute = false;
m_mousex = 0;
m_mousey = 0;
@@ -1347,7 +1365,7 @@ ConsoleVRDPServer::ConsoleVRDPServer(Console *console)
m_InputSynch.fClientCapsLock = false;
m_InputSynch.fClientScrollLock = false;
- memset(maFramebuffers, 0, sizeof(maFramebuffers));
+ RT_ZERO(maFramebuffers);
{
ComPtr<IEventSource> es;
@@ -1368,18 +1386,29 @@ ConsoleVRDPServer::ConsoleVRDPServer(Console *console)
mAuthLibrary = 0;
mu32AudioInputClientId = 0;
+ mcClients = 0;
/*
* Optional interfaces.
*/
m_fInterfaceImage = false;
- memset(&m_interfaceImage, 0, sizeof (m_interfaceImage));
- memset(&m_interfaceCallbacksImage, 0, sizeof (m_interfaceCallbacksImage));
+ RT_ZERO(m_interfaceImage);
+ RT_ZERO(m_interfaceCallbacksImage);
RT_ZERO(m_interfaceMousePtr);
RT_ZERO(m_interfaceSCard);
RT_ZERO(m_interfaceCallbacksSCard);
RT_ZERO(m_interfaceTSMF);
RT_ZERO(m_interfaceCallbacksTSMF);
+ RT_ZERO(m_interfaceVideoIn);
+ RT_ZERO(m_interfaceCallbacksVideoIn);
+ RT_ZERO(m_interfaceInput);
+ RT_ZERO(m_interfaceCallbacksInput);
+
+ rc = RTCritSectInit(&mTSMFLock);
+ AssertRC(rc);
+
+ mEmWebcam = new EmWebcam(this);
+ AssertPtr(mEmWebcam);
}
ConsoleVRDPServer::~ConsoleVRDPServer()
@@ -1404,10 +1433,22 @@ ConsoleVRDPServer::~ConsoleVRDPServer()
}
}
+ if (mEmWebcam)
+ {
+ delete mEmWebcam;
+ mEmWebcam = NULL;
+ }
+
if (RTCritSectIsInitialized(&mCritSect))
{
RTCritSectDelete(&mCritSect);
- memset(&mCritSect, 0, sizeof(mCritSect));
+ RT_ZERO(mCritSect);
+ }
+
+ if (RTCritSectIsInitialized(&mTSMFLock))
+ {
+ RTCritSectDelete(&mTSMFLock);
+ RT_ZERO(mTSMFLock);
}
}
@@ -1654,6 +1695,54 @@ int ConsoleVRDPServer::Launch(void)
RT_ZERO(m_interfaceTSMF);
}
+ /* VideoIn interface. */
+ m_interfaceVideoIn.header.u64Version = 1;
+ m_interfaceVideoIn.header.u64Size = sizeof(m_interfaceVideoIn);
+
+ m_interfaceCallbacksVideoIn.header.u64Version = 1;
+ m_interfaceCallbacksVideoIn.header.u64Size = sizeof(m_interfaceCallbacksVideoIn);
+ m_interfaceCallbacksVideoIn.VRDECallbackVideoInNotify = VRDECallbackVideoInNotify;
+ m_interfaceCallbacksVideoIn.VRDECallbackVideoInDeviceDesc = VRDECallbackVideoInDeviceDesc;
+ m_interfaceCallbacksVideoIn.VRDECallbackVideoInControl = VRDECallbackVideoInControl;
+ m_interfaceCallbacksVideoIn.VRDECallbackVideoInFrame = VRDECallbackVideoInFrame;
+
+ vrc = mpEntryPoints->VRDEGetInterface(mhServer,
+ VRDE_VIDEOIN_INTERFACE_NAME,
+ &m_interfaceVideoIn.header,
+ &m_interfaceCallbacksVideoIn.header,
+ this);
+ if (RT_SUCCESS(vrc))
+ {
+ LogRel(("VRDE: [%s]\n", VRDE_VIDEOIN_INTERFACE_NAME));
+ }
+ else
+ {
+ RT_ZERO(m_interfaceVideoIn);
+ }
+
+ /* Input interface. */
+ m_interfaceInput.header.u64Version = 1;
+ m_interfaceInput.header.u64Size = sizeof(m_interfaceInput);
+
+ m_interfaceCallbacksInput.header.u64Version = 1;
+ m_interfaceCallbacksInput.header.u64Size = sizeof(m_interfaceCallbacksInput);
+ m_interfaceCallbacksInput.VRDECallbackInputSetup = VRDECallbackInputSetup;
+ m_interfaceCallbacksInput.VRDECallbackInputEvent = VRDECallbackInputEvent;
+
+ vrc = mpEntryPoints->VRDEGetInterface(mhServer,
+ VRDE_INPUT_INTERFACE_NAME,
+ &m_interfaceInput.header,
+ &m_interfaceCallbacksInput.header,
+ this);
+ if (RT_SUCCESS(vrc))
+ {
+ LogRel(("VRDE: [%s]\n", VRDE_INPUT_INTERFACE_NAME));
+ }
+ else
+ {
+ RT_ZERO(m_interfaceInput);
+ }
+
/* Since these interfaces are optional, it is always a success here. */
vrc = VINF_SUCCESS;
}
@@ -1683,14 +1772,17 @@ typedef struct H3DORInstance
uint32_t w;
uint32_t h;
bool fCreated;
+ bool fFallback;
} H3DORInstance;
+#define H3DORLOG Log
+
/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORBegin(const void *pvContext, void **ppvInstance,
const char *pszFormat)
{
- LogFlowFunc(("ctx %p\n", pvContext));
+ H3DORLOG(("H3DORBegin: ctx %p [%s]\n", pvContext, pszFormat));
- H3DORInstance *p = (H3DORInstance *)RTMemAlloc(sizeof (H3DORInstance));
+ H3DORInstance *p = (H3DORInstance *)RTMemAlloc(sizeof(H3DORInstance));
if (p)
{
@@ -1701,6 +1793,7 @@ typedef struct H3DORInstance
p->w = 0;
p->h = 0;
p->fCreated = false;
+ p->fFallback = false;
/* Host 3D service passes the actual format of data in this redirect instance.
* That is what will be in the H3DORFrame's parameters pvData and cbData.
@@ -1716,14 +1809,16 @@ typedef struct H3DORInstance
}
}
- /* Caller check this for NULL. */
+ H3DORLOG(("H3DORBegin: ins %p\n", p));
+
+ /* Caller checks this for NULL. */
*ppvInstance = p;
}
/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORGeometry(void *pvInstance,
int32_t x, int32_t y, uint32_t w, uint32_t h)
{
- LogFlowFunc(("ins %p %d,%d %dx%d\n", pvInstance, x, y, w, h));
+ H3DORLOG(("H3DORGeometry: ins %p %d,%d %dx%d\n", pvInstance, x, y, w, h));
H3DORInstance *p = (H3DORInstance *)pvInstance;
Assert(p);
@@ -1752,12 +1847,14 @@ typedef struct H3DORInstance
&& p->w == w
&& p->h == h)
{
- LogFlowFunc(("geometry not changed\n"));
+ H3DORLOG(("H3DORGeometry: geometry not changed\n"));
/* Do nothing. Continue using the existing handle. */
}
else
{
- int rc = p->pThis->m_interfaceImage.VRDEImageGeometrySet(p->hImageBitmap, &rect);
+ int rc = p->fFallback?
+ VERR_NOT_SUPPORTED: /* Try to go out of fallback mode. */
+ p->pThis->m_interfaceImage.VRDEImageGeometrySet(p->hImageBitmap, &rect);
if (RT_SUCCESS(rc))
{
p->x = x;
@@ -1786,6 +1883,7 @@ typedef struct H3DORInstance
* the clipping must be done here in ConsoleVRDPServer
*/
uint32_t fu32CompletionFlags = 0;
+ p->fFallback = false;
int rc = p->pThis->m_interfaceImage.VRDEImageHandleCreate(p->pThis->mhServer,
&p->hImageBitmap,
p,
@@ -1800,7 +1898,9 @@ typedef struct H3DORInstance
if (RT_FAILURE(rc))
{
/* No support for a 3D + WINDOW. Try bitmap updates. */
+ H3DORLOG(("H3DORGeometry: Fallback to bitmaps\n"));
fu32CompletionFlags = 0;
+ p->fFallback = true;
rc = p->pThis->m_interfaceImage.VRDEImageHandleCreate(p->pThis->mhServer,
&p->hImageBitmap,
p,
@@ -1813,6 +1913,8 @@ typedef struct H3DORInstance
&fu32CompletionFlags);
}
+ H3DORLOG(("H3DORGeometry: Image handle create %Rrc, flags 0x%RX32\n", rc, fu32CompletionFlags));
+
if (RT_SUCCESS(rc))
{
p->x = x;
@@ -1832,12 +1934,14 @@ typedef struct H3DORInstance
p->h = 0;
}
}
+
+ H3DORLOG(("H3DORGeometry: ins %p completed\n", pvInstance));
}
/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORVisibleRegion(void *pvInstance,
- uint32_t cRects, RTRECT *paRects)
+ uint32_t cRects, const RTRECT *paRects)
{
- LogFlowFunc(("ins %p %d\n", pvInstance, cRects));
+ H3DORLOG(("H3DORVisibleRegion: ins %p %d\n", pvInstance, cRects));
H3DORInstance *p = (H3DORInstance *)pvInstance;
Assert(p);
@@ -1861,12 +1965,14 @@ typedef struct H3DORInstance
cRects,
paRects);
}
+
+ H3DORLOG(("H3DORVisibleRegion: ins %p completed\n", pvInstance));
}
/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORFrame(void *pvInstance,
void *pvData, uint32_t cbData)
{
- LogFlowFunc(("ins %p %p %d\n", pvInstance, pvData, cbData));
+ H3DORLOG(("H3DORFrame: ins %p %p %d\n", pvInstance, pvData, cbData));
H3DORInstance *p = (H3DORInstance *)pvInstance;
Assert(p);
@@ -1889,11 +1995,13 @@ typedef struct H3DORInstance
p->h,
&image,
sizeof(VRDEIMAGEBITMAP));
+
+ H3DORLOG(("H3DORFrame: ins %p completed\n", pvInstance));
}
/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DOREnd(void *pvInstance)
{
- LogFlowFunc(("ins %p\n", pvInstance));
+ H3DORLOG(("H3DOREnd: ins %p\n", pvInstance));
H3DORInstance *p = (H3DORInstance *)pvInstance;
Assert(p);
@@ -1902,6 +2010,8 @@ typedef struct H3DORInstance
p->pThis->m_interfaceImage.VRDEImageHandleClose(p->hImageBitmap);
RTMemFree(p);
+
+ H3DORLOG(("H3DOREnd: ins %p completed\n", pvInstance));
}
/* static */ DECLCALLBACK(int) ConsoleVRDPServer::H3DORContextProperty(const void *pvContext, uint32_t index,
@@ -1909,6 +2019,8 @@ typedef struct H3DORInstance
{
int rc = VINF_SUCCESS;
+ H3DORLOG(("H3DORContextProperty: index %d\n", index));
+
if (index == H3DOR_PROP_FORMATS)
{
/* Return a comma separated list of supported formats. */
@@ -1929,10 +2041,11 @@ typedef struct H3DORInstance
rc = VERR_NOT_SUPPORTED;
}
+ H3DORLOG(("H3DORContextProperty: %Rrc\n", rc));
return rc;
}
-void ConsoleVRDPServer::remote3DRedirect(void)
+void ConsoleVRDPServer::remote3DRedirect(bool fEnable)
{
if (!m_fInterfaceImage)
{
@@ -1940,21 +2053,17 @@ void ConsoleVRDPServer::remote3DRedirect(void)
return;
}
- /* Check if 3D redirection has been enabled. */
+ /* Check if 3D redirection has been enabled. It is enabled by default. */
com::Bstr bstr;
HRESULT hrc = mConsole->getVRDEServer()->GetVRDEProperty(Bstr("H3DRedirect/Enabled").raw(), bstr.asOutParam());
- if (hrc != S_OK)
- {
- bstr = "";
- }
-
- com::Utf8Str value = bstr;
+ com::Utf8Str value = hrc == S_OK? bstr: "";
- bool fEnabled = RTStrICmp(value.c_str(), "true") == 0
- || RTStrICmp(value.c_str(), "1") == 0;
+ bool fAllowed = RTStrICmp(value.c_str(), "true") == 0
+ || RTStrICmp(value.c_str(), "1") == 0
+ || value.c_str()[0] == 0;
- if (!fEnabled)
+ if (!fAllowed && fEnable)
{
return;
}
@@ -1971,6 +2080,12 @@ void ConsoleVRDPServer::remote3DRedirect(void)
H3DORContextProperty
};
+ if (!fEnable)
+ {
+ /* This will tell the service to disable rediection. */
+ RT_ZERO(outputRedirect);
+ }
+
VBOXHGCMSVCPARM parm;
parm.type = VBOX_HGCM_SVC_PARM_PTR;
@@ -1992,11 +2107,11 @@ void ConsoleVRDPServer::remote3DRedirect(void)
if (!RT_SUCCESS(rc))
{
- AssertMsgFailed(("SHCRGL_HOST_FN_SET_CONSOLE failed with %Rrc\n", rc));
+ Log(("SHCRGL_HOST_FN_SET_CONSOLE failed with %Rrc\n", rc));
return;
}
- LogRel(("VRDE: Enabled 3D redirect.\n"));
+ LogRel(("VRDE: %s 3D redirect.\n", fEnable? "Enabled": "Disabled"));
return;
}
@@ -2008,8 +2123,8 @@ void ConsoleVRDPServer::remote3DRedirect(void)
void *pvData,
uint32_t cbData)
{
- LogFlowFunc(("pvContext %p, pvUser %p, hVideo %p, u32Id %u, pvData %p, cbData %d\n",
- pvContext, pvUser, hVideo, u32Id, pvData, cbData));
+ H3DORLOG(("H3DOR: VRDEImageCbNotify: pvContext %p, pvUser %p, hVideo %p, u32Id %u, pvData %p, cbData %d\n",
+ pvContext, pvUser, hVideo, u32Id, pvData, cbData));
ConsoleVRDPServer *pServer = static_cast<ConsoleVRDPServer*>(pvContext);
H3DORInstance *p = (H3DORInstance *)pvUser;
@@ -2026,8 +2141,8 @@ void ConsoleVRDPServer::remote3DRedirect(void)
}
uint32_t u32StreamId = *(uint32_t *)pvData;
- LogFlowFunc(("VRDE_IMAGE_NOTIFY_HANDLE_CREATE u32StreamId %d\n",
- u32StreamId));
+ H3DORLOG(("H3DOR: VRDE_IMAGE_NOTIFY_HANDLE_CREATE u32StreamId %d\n",
+ u32StreamId));
if (u32StreamId != 0)
{
@@ -2042,6 +2157,8 @@ void ConsoleVRDPServer::remote3DRedirect(void)
return VINF_SUCCESS;
}
+#undef H3DORLOG
+
/* static */ DECLCALLBACK(int) ConsoleVRDPServer::VRDESCardCbNotify(void *pvContext,
uint32_t u32Id,
void *pvData,
@@ -2098,19 +2215,64 @@ int ConsoleVRDPServer::SCardRequest(void *pvUser, uint32_t u32Function, const vo
return rc;
}
-typedef struct TSMFHOSTCHANNELCTX
+
+struct TSMFHOSTCHCTX;
+struct TSMFVRDPCTX;
+
+typedef struct TSMFHOSTCHCTX
+{
+ ConsoleVRDPServer *pThis;
+
+ struct TSMFVRDPCTX *pVRDPCtx; /* NULL if no corresponding host channel context. */
+
+ void *pvDataReceived;
+ uint32_t cbDataReceived;
+ uint32_t cbDataAllocated;
+} TSMFHOSTCHCTX;
+
+typedef struct TSMFVRDPCTX
{
ConsoleVRDPServer *pThis;
VBOXHOSTCHANNELCALLBACKS *pCallbacks;
void *pvCallbacks;
+ TSMFHOSTCHCTX *pHostChCtx; /* NULL if no corresponding host channel context. */
+
uint32_t u32ChannelHandle;
+} TSMFVRDPCTX;
- void *pvDataReceived;
- uint32_t cbDataReceived;
- uint32_t cbDataAllocated;
-} TSMFHOSTCHANNELCTX;
+static int tsmfContextsAlloc(TSMFHOSTCHCTX **ppHostChCtx, TSMFVRDPCTX **ppVRDPCtx)
+{
+ TSMFHOSTCHCTX *pHostChCtx = (TSMFHOSTCHCTX *)RTMemAllocZ(sizeof(TSMFHOSTCHCTX));
+ if (!pHostChCtx)
+ {
+ return VERR_NO_MEMORY;
+ }
+
+ TSMFVRDPCTX *pVRDPCtx = (TSMFVRDPCTX *)RTMemAllocZ(sizeof(TSMFVRDPCTX));
+ if (!pVRDPCtx)
+ {
+ RTMemFree(pHostChCtx);
+ return VERR_NO_MEMORY;
+ }
+
+ *ppHostChCtx = pHostChCtx;
+ *ppVRDPCtx = pVRDPCtx;
+ return VINF_SUCCESS;
+}
+
+int ConsoleVRDPServer::tsmfLock(void)
+{
+ int rc = RTCritSectEnter(&mTSMFLock);
+ AssertRC(rc);
+ return rc;
+}
+
+void ConsoleVRDPServer::tsmfUnlock(void)
+{
+ RTCritSectLeave(&mTSMFLock);
+}
/* static */ DECLCALLBACK(int) ConsoleVRDPServer::tsmfHostChannelAttach(void *pvProvider,
void **ppvChannel,
@@ -2122,26 +2284,35 @@ typedef struct TSMFHOSTCHANNELCTX
ConsoleVRDPServer *pThis = static_cast<ConsoleVRDPServer*>(pvProvider);
- TSMFHOSTCHANNELCTX *pCtx = (TSMFHOSTCHANNELCTX *)RTMemAllocZ(sizeof(TSMFHOSTCHANNELCTX));
- if (!pCtx)
+ /* Create 2 context structures: for the VRDP server and for the host service. */
+ TSMFHOSTCHCTX *pHostChCtx = NULL;
+ TSMFVRDPCTX *pVRDPCtx = NULL;
+
+ int rc = tsmfContextsAlloc(&pHostChCtx, &pVRDPCtx);
+ if (RT_FAILURE(rc))
{
- return VERR_NO_MEMORY;
+ return rc;
}
- pCtx->pThis = pThis;
- pCtx->pCallbacks = pCallbacks;
- pCtx->pvCallbacks = pvCallbacks;
+ pHostChCtx->pThis = pThis;
+ pHostChCtx->pVRDPCtx = pVRDPCtx;
- int rc = pThis->m_interfaceTSMF.VRDETSMFChannelCreate(pThis->mhServer, pCtx, u32Flags);
+ pVRDPCtx->pThis = pThis;
+ pVRDPCtx->pCallbacks = pCallbacks;
+ pVRDPCtx->pvCallbacks = pvCallbacks;
+ pVRDPCtx->pHostChCtx = pHostChCtx;
+
+ rc = pThis->m_interfaceTSMF.VRDETSMFChannelCreate(pThis->mhServer, pVRDPCtx, u32Flags);
if (RT_SUCCESS(rc))
{
/* @todo contexts should be in a list for accounting. */
- *ppvChannel = pCtx;
+ *ppvChannel = pHostChCtx;
}
else
{
- RTMemFree(pCtx);
+ RTMemFree(pHostChCtx);
+ RTMemFree(pVRDPCtx);
}
return rc;
@@ -2151,21 +2322,69 @@ typedef struct TSMFHOSTCHANNELCTX
{
LogFlowFunc(("\n"));
- TSMFHOSTCHANNELCTX *pCtx = (TSMFHOSTCHANNELCTX *)pvChannel;
+ TSMFHOSTCHCTX *pHostChCtx = (TSMFHOSTCHCTX *)pvChannel;
+ ConsoleVRDPServer *pThis = pHostChCtx->pThis;
+
+ int rc = pThis->tsmfLock();
+ if (RT_SUCCESS(rc))
+ {
+ bool fClose = false;
+ uint32_t u32ChannelHandle = 0;
+
+ if (pHostChCtx->pVRDPCtx)
+ {
+ /* There is still a VRDP context for this channel. */
+ pHostChCtx->pVRDPCtx->pHostChCtx = NULL;
+ u32ChannelHandle = pHostChCtx->pVRDPCtx->u32ChannelHandle;
+ fClose = true;
+ }
+
+ pThis->tsmfUnlock();
- pCtx->pThis->m_interfaceTSMF.VRDETSMFChannelClose(pCtx->pThis->mhServer, pCtx->u32ChannelHandle);
- /* @todo */
+ RTMemFree(pHostChCtx);
+
+ if (fClose)
+ {
+ LogFlowFunc(("Closing VRDE channel %d.\n", u32ChannelHandle));
+ pThis->m_interfaceTSMF.VRDETSMFChannelClose(pThis->mhServer, u32ChannelHandle);
+ }
+ else
+ {
+ LogFlowFunc(("No VRDE channel.\n"));
+ }
+ }
}
/* static */ DECLCALLBACK(int) ConsoleVRDPServer::tsmfHostChannelSend(void *pvChannel,
const void *pvData,
uint32_t cbData)
{
- LogFlowFunc(("\n"));
- TSMFHOSTCHANNELCTX *pCtx = (TSMFHOSTCHANNELCTX *)pvChannel;
+ LogFlowFunc(("cbData %d\n", cbData));
+
+ TSMFHOSTCHCTX *pHostChCtx = (TSMFHOSTCHCTX *)pvChannel;
+ ConsoleVRDPServer *pThis = pHostChCtx->pThis;
+
+ int rc = pThis->tsmfLock();
+ if (RT_SUCCESS(rc))
+ {
+ bool fSend = false;
+ uint32_t u32ChannelHandle = 0;
+
+ if (pHostChCtx->pVRDPCtx)
+ {
+ u32ChannelHandle = pHostChCtx->pVRDPCtx->u32ChannelHandle;
+ fSend = true;
+ }
- int rc = pCtx->pThis->m_interfaceTSMF.VRDETSMFChannelSend(pCtx->pThis->mhServer, pCtx->u32ChannelHandle,
- pvData, cbData);
+ pThis->tsmfUnlock();
+
+ if (fSend)
+ {
+ LogFlowFunc(("Send to VRDE channel %d.\n", u32ChannelHandle));
+ rc = pThis->m_interfaceTSMF.VRDETSMFChannelSend(pThis->mhServer, u32ChannelHandle,
+ pvData, cbData);
+ }
+ }
return rc;
}
@@ -2176,32 +2395,38 @@ typedef struct TSMFHOSTCHANNELCTX
uint32_t *pcbReceived,
uint32_t *pcbRemaining)
{
- LogFlowFunc(("\n"));
-
- TSMFHOSTCHANNELCTX *pCtx = (TSMFHOSTCHANNELCTX *)pvChannel;
- int rc = VINF_SUCCESS;
+ LogFlowFunc(("cbData %d\n", cbData));
- uint32_t cbToCopy = RT_MIN(cbData, pCtx->cbDataReceived);
- uint32_t cbRemaining = pCtx->cbDataReceived - cbToCopy;
+ TSMFHOSTCHCTX *pHostChCtx = (TSMFHOSTCHCTX *)pvChannel;
+ ConsoleVRDPServer *pThis = pHostChCtx->pThis;
- LogFlowFunc(("cbToCopy %d, cbRemaining %d\n", cbToCopy, cbRemaining));
-
- if (cbToCopy != 0)
+ int rc = pThis->tsmfLock();
+ if (RT_SUCCESS(rc))
{
- memcpy(pvData, pCtx->pvDataReceived, cbToCopy);
+ uint32_t cbToCopy = RT_MIN(cbData, pHostChCtx->cbDataReceived);
+ uint32_t cbRemaining = pHostChCtx->cbDataReceived - cbToCopy;
+
+ LogFlowFunc(("cbToCopy %d, cbRemaining %d\n", cbToCopy, cbRemaining));
- if (cbRemaining != 0)
+ if (cbToCopy != 0)
{
- memmove(pCtx->pvDataReceived, (uint8_t *)pCtx->pvDataReceived + cbToCopy, cbRemaining);
+ memcpy(pvData, pHostChCtx->pvDataReceived, cbToCopy);
+
+ if (cbRemaining != 0)
+ {
+ memmove(pHostChCtx->pvDataReceived, (uint8_t *)pHostChCtx->pvDataReceived + cbToCopy, cbRemaining);
+ }
+
+ pHostChCtx->cbDataReceived = cbRemaining;
}
- pCtx->cbDataReceived = cbRemaining;
- }
+ pThis->tsmfUnlock();
- *pcbRemaining = cbRemaining;
- *pcbReceived = cbToCopy;
+ *pcbRemaining = cbRemaining;
+ *pcbReceived = cbToCopy;
+ }
- return VINF_SUCCESS;
+ return rc;
}
/* static */ DECLCALLBACK(int) ConsoleVRDPServer::tsmfHostChannelControl(void *pvChannel,
@@ -2212,7 +2437,8 @@ typedef struct TSMFHOSTCHANNELCTX
uint32_t cbData,
uint32_t *pcbDataReturned)
{
- LogFlowFunc(("\n"));
+ LogFlowFunc(("u32Code %u\n", u32Code));
+
if (!pvChannel)
{
/* Special case, the provider must answer rather than a channel instance. */
@@ -2295,33 +2521,44 @@ void ConsoleVRDPServer::setupTSMF(void)
const void *pvParm,
uint32_t cbParm)
{
+ int rc = VINF_SUCCESS;
+
ConsoleVRDPServer *pThis = static_cast<ConsoleVRDPServer*>(pvContext);
- TSMFHOSTCHANNELCTX *pCtx = (TSMFHOSTCHANNELCTX *)pvChannel;
+ TSMFVRDPCTX *pVRDPCtx = (TSMFVRDPCTX *)pvChannel;
- Assert(pCtx->pThis == pThis);
+ Assert(pVRDPCtx->pThis == pThis);
- switch(u32Notification)
+ if (pVRDPCtx->pCallbacks == NULL)
+ {
+ LogFlowFunc(("tsmfHostChannel: Channel disconnected. Skipping.\n"));
+ return;
+ }
+
+ switch (u32Notification)
{
case VRDE_TSMF_N_CREATE_ACCEPTED:
{
VRDETSMFNOTIFYCREATEACCEPTED *p = (VRDETSMFNOTIFYCREATEACCEPTED *)pvParm;
Assert(cbParm == sizeof(VRDETSMFNOTIFYCREATEACCEPTED));
- LogFlowFunc(("VRDE_TSMF_N_CREATE_ACCEPTED: p->u32ChannelHandle %d\n", p->u32ChannelHandle));
+ LogFlowFunc(("tsmfHostChannel: VRDE_TSMF_N_CREATE_ACCEPTED(%p): p->u32ChannelHandle %d\n",
+ pVRDPCtx, p->u32ChannelHandle));
- pCtx->u32ChannelHandle = p->u32ChannelHandle;
+ pVRDPCtx->u32ChannelHandle = p->u32ChannelHandle;
- pCtx->pCallbacks->HostChannelCallbackEvent(pCtx->pvCallbacks, pCtx,
- VBOX_TSMF_HCH_CREATE_ACCEPTED,
- NULL, 0);
+ pVRDPCtx->pCallbacks->HostChannelCallbackEvent(pVRDPCtx->pvCallbacks, pVRDPCtx->pHostChCtx,
+ VBOX_TSMF_HCH_CREATE_ACCEPTED,
+ NULL, 0);
} break;
case VRDE_TSMF_N_CREATE_DECLINED:
{
- pCtx->pCallbacks->HostChannelCallbackEvent(pCtx->pvCallbacks, pCtx,
- VBOX_TSMF_HCH_CREATE_DECLINED,
- NULL, 0);
+ LogFlowFunc(("tsmfHostChannel: VRDE_TSMF_N_CREATE_DECLINED(%p)\n", pVRDPCtx));
+
+ pVRDPCtx->pCallbacks->HostChannelCallbackEvent(pVRDPCtx->pvCallbacks, pVRDPCtx->pHostChCtx,
+ VBOX_TSMF_HCH_CREATE_DECLINED,
+ NULL, 0);
} break;
case VRDE_TSMF_N_DATA:
@@ -2330,39 +2567,79 @@ void ConsoleVRDPServer::setupTSMF(void)
VRDETSMFNOTIFYDATA *p = (VRDETSMFNOTIFYDATA *)pvParm;
Assert(cbParm == sizeof(VRDETSMFNOTIFYDATA));
- LogFlowFunc(("VRDE_TSMF_N_DATA: p->cbData %d\n", p->cbData));
+ LogFlowFunc(("tsmfHostChannel: VRDE_TSMF_N_DATA(%p): p->cbData %d\n", pVRDPCtx, p->cbData));
- if (pCtx->pvDataReceived)
- {
- uint32_t cbAlloc = p->cbData + pCtx->cbDataReceived;
- pCtx->pvDataReceived = RTMemRealloc(pCtx->pvDataReceived, cbAlloc);
- memcpy((uint8_t *)pCtx->pvDataReceived + pCtx->cbDataReceived, p->pvData, p->cbData);
+ VBOXHOSTCHANNELEVENTRECV ev;
+ ev.u32SizeAvailable = 0;
- pCtx->cbDataReceived += p->cbData;
- pCtx->cbDataAllocated = cbAlloc;
- }
- else
+ rc = pThis->tsmfLock();
+
+ if (RT_SUCCESS(rc))
{
- pCtx->pvDataReceived = RTMemAlloc(p->cbData);
- memcpy(pCtx->pvDataReceived, p->pvData, p->cbData);
+ TSMFHOSTCHCTX *pHostChCtx = pVRDPCtx->pHostChCtx;
- pCtx->cbDataReceived = p->cbData;
- pCtx->cbDataAllocated = p->cbData;
- }
+ if (pHostChCtx)
+ {
+ if (pHostChCtx->pvDataReceived)
+ {
+ uint32_t cbAlloc = p->cbData + pHostChCtx->cbDataReceived;
+ pHostChCtx->pvDataReceived = RTMemRealloc(pHostChCtx->pvDataReceived, cbAlloc);
+ memcpy((uint8_t *)pHostChCtx->pvDataReceived + pHostChCtx->cbDataReceived, p->pvData, p->cbData);
- VBOXHOSTCHANNELEVENTRECV ev;
- ev.u32SizeAvailable = p->cbData;
+ pHostChCtx->cbDataReceived += p->cbData;
+ pHostChCtx->cbDataAllocated = cbAlloc;
+ }
+ else
+ {
+ pHostChCtx->pvDataReceived = RTMemAlloc(p->cbData);
+ memcpy(pHostChCtx->pvDataReceived, p->pvData, p->cbData);
- pCtx->pCallbacks->HostChannelCallbackEvent(pCtx->pvCallbacks, pCtx,
- VBOX_HOST_CHANNEL_EVENT_RECV,
- &ev, sizeof(ev));
+ pHostChCtx->cbDataReceived = p->cbData;
+ pHostChCtx->cbDataAllocated = p->cbData;
+ }
+
+ ev.u32SizeAvailable = p->cbData;
+ }
+ else
+ {
+ LogFlowFunc(("tsmfHostChannel: VRDE_TSMF_N_DATA: no host channel. Skipping\n"));
+ }
+
+ pThis->tsmfUnlock();
+ }
+
+ pVRDPCtx->pCallbacks->HostChannelCallbackEvent(pVRDPCtx->pvCallbacks, pVRDPCtx->pHostChCtx,
+ VBOX_HOST_CHANNEL_EVENT_RECV,
+ &ev, sizeof(ev));
} break;
case VRDE_TSMF_N_DISCONNECTED:
{
- pCtx->pCallbacks->HostChannelCallbackEvent(pCtx->pvCallbacks, pCtx,
- VBOX_TSMF_HCH_DISCONNECTED,
- NULL, 0);
+ LogFlowFunc(("tsmfHostChannel: VRDE_TSMF_N_DISCONNECTED(%p)\n", pVRDPCtx));
+
+ pVRDPCtx->pCallbacks->HostChannelCallbackEvent(pVRDPCtx->pvCallbacks, pVRDPCtx->pHostChCtx,
+ VBOX_TSMF_HCH_DISCONNECTED,
+ NULL, 0);
+
+ /* The callback context will not be used anymore. */
+ pVRDPCtx->pCallbacks->HostChannelCallbackDeleted(pVRDPCtx->pvCallbacks, pVRDPCtx->pHostChCtx);
+ pVRDPCtx->pCallbacks = NULL;
+ pVRDPCtx->pvCallbacks = NULL;
+
+ rc = pThis->tsmfLock();
+ if (RT_SUCCESS(rc))
+ {
+ if (pVRDPCtx->pHostChCtx)
+ {
+ /* There is still a host channel context for this channel. */
+ pVRDPCtx->pHostChCtx->pVRDPCtx = NULL;
+ }
+
+ pThis->tsmfUnlock();
+
+ RT_ZERO(*pVRDPCtx);
+ RTMemFree(pVRDPCtx);
+ }
} break;
default:
@@ -2372,15 +2649,222 @@ void ConsoleVRDPServer::setupTSMF(void)
}
}
+/* static */ DECLCALLBACK(void) ConsoleVRDPServer::VRDECallbackVideoInNotify(void *pvCallback,
+ uint32_t u32Id,
+ const void *pvData,
+ uint32_t cbData)
+{
+ ConsoleVRDPServer *pThis = static_cast<ConsoleVRDPServer*>(pvCallback);
+ if (pThis->mEmWebcam)
+ {
+ pThis->mEmWebcam->EmWebcamCbNotify(u32Id, pvData, cbData);
+ }
+}
+
+/* static */ DECLCALLBACK(void) ConsoleVRDPServer::VRDECallbackVideoInDeviceDesc(void *pvCallback,
+ int rcRequest,
+ void *pDeviceCtx,
+ void *pvUser,
+ const VRDEVIDEOINDEVICEDESC *pDeviceDesc,
+ uint32_t cbDevice)
+{
+ ConsoleVRDPServer *pThis = static_cast<ConsoleVRDPServer*>(pvCallback);
+ if (pThis->mEmWebcam)
+ {
+ pThis->mEmWebcam->EmWebcamCbDeviceDesc(rcRequest, pDeviceCtx, pvUser, pDeviceDesc, cbDevice);
+ }
+}
+
+/* static */ DECLCALLBACK(void) ConsoleVRDPServer::VRDECallbackVideoInControl(void *pvCallback,
+ int rcRequest,
+ void *pDeviceCtx,
+ void *pvUser,
+ const VRDEVIDEOINCTRLHDR *pControl,
+ uint32_t cbControl)
+{
+ ConsoleVRDPServer *pThis = static_cast<ConsoleVRDPServer*>(pvCallback);
+ if (pThis->mEmWebcam)
+ {
+ pThis->mEmWebcam->EmWebcamCbControl(rcRequest, pDeviceCtx, pvUser, pControl, cbControl);
+ }
+}
+
+/* static */ DECLCALLBACK(void) ConsoleVRDPServer::VRDECallbackVideoInFrame(void *pvCallback,
+ int rcRequest,
+ void *pDeviceCtx,
+ const VRDEVIDEOINPAYLOADHDR *pFrame,
+ uint32_t cbFrame)
+{
+ ConsoleVRDPServer *pThis = static_cast<ConsoleVRDPServer*>(pvCallback);
+ if (pThis->mEmWebcam)
+ {
+ pThis->mEmWebcam->EmWebcamCbFrame(rcRequest, pDeviceCtx, pFrame, cbFrame);
+ }
+}
+
+int ConsoleVRDPServer::VideoInDeviceAttach(const VRDEVIDEOINDEVICEHANDLE *pDeviceHandle, void *pvDeviceCtx)
+{
+ int rc;
+
+ if (mhServer && mpEntryPoints && m_interfaceVideoIn.VRDEVideoInDeviceAttach)
+ {
+ rc = m_interfaceVideoIn.VRDEVideoInDeviceAttach(mhServer, pDeviceHandle, pvDeviceCtx);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int ConsoleVRDPServer::VideoInDeviceDetach(const VRDEVIDEOINDEVICEHANDLE *pDeviceHandle)
+{
+ int rc;
+
+ if (mhServer && mpEntryPoints && m_interfaceVideoIn.VRDEVideoInDeviceDetach)
+ {
+ rc = m_interfaceVideoIn.VRDEVideoInDeviceDetach(mhServer, pDeviceHandle);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int ConsoleVRDPServer::VideoInGetDeviceDesc(void *pvUser, const VRDEVIDEOINDEVICEHANDLE *pDeviceHandle)
+{
+ int rc;
+
+ if (mhServer && mpEntryPoints && m_interfaceVideoIn.VRDEVideoInGetDeviceDesc)
+ {
+ rc = m_interfaceVideoIn.VRDEVideoInGetDeviceDesc(mhServer, pvUser, pDeviceHandle);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int ConsoleVRDPServer::VideoInControl(void *pvUser, const VRDEVIDEOINDEVICEHANDLE *pDeviceHandle,
+ const VRDEVIDEOINCTRLHDR *pReq, uint32_t cbReq)
+{
+ int rc;
+
+ if (mhServer && mpEntryPoints && m_interfaceVideoIn.VRDEVideoInControl)
+ {
+ rc = m_interfaceVideoIn.VRDEVideoInControl(mhServer, pvUser, pDeviceHandle, pReq, cbReq);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+
+/* static */ DECLCALLBACK(void) ConsoleVRDPServer::VRDECallbackInputSetup(void *pvCallback,
+ int rcRequest,
+ uint32_t u32Method,
+ const void *pvResult,
+ uint32_t cbResult)
+{
+ NOREF(pvCallback);
+ NOREF(rcRequest);
+ NOREF(u32Method);
+ NOREF(pvResult);
+ NOREF(cbResult);
+}
+
+/* static */ DECLCALLBACK(void) ConsoleVRDPServer::VRDECallbackInputEvent(void *pvCallback,
+ uint32_t u32Method,
+ const void *pvEvent,
+ uint32_t cbEvent)
+{
+ ConsoleVRDPServer *pThis = static_cast<ConsoleVRDPServer*>(pvCallback);
+
+ if (u32Method == VRDE_INPUT_METHOD_TOUCH)
+ {
+ if (cbEvent >= sizeof(VRDEINPUTHEADER))
+ {
+ VRDEINPUTHEADER *pHeader = (VRDEINPUTHEADER *)pvEvent;
+
+ if (pHeader->u16EventId == VRDEINPUT_EVENTID_TOUCH)
+ {
+ IMouse *pMouse = pThis->mConsole->getMouse();
+
+ VRDEINPUT_TOUCH_EVENT_PDU *p = (VRDEINPUT_TOUCH_EVENT_PDU *)pHeader;
+
+ uint16_t iFrame;
+ for (iFrame = 0; iFrame < p->u16FrameCount; iFrame++)
+ {
+ VRDEINPUT_TOUCH_FRAME *pFrame = &p->aFrames[iFrame];
+
+ com::SafeArray<LONG64> aContacts(pFrame->u16ContactCount);
+
+ uint16_t iContact;
+ for (iContact = 0; iContact < pFrame->u16ContactCount; iContact++)
+ {
+ VRDEINPUT_CONTACT_DATA *pContact = &pFrame->aContacts[iContact];
+
+ int16_t x = (int16_t)(pContact->i32X + 1);
+ int16_t y = (int16_t)(pContact->i32Y + 1);
+ uint8_t contactId = pContact->u8ContactId;
+ uint8_t contactState = TouchContactState_None;
+
+ if (pContact->u32ContactFlags & VRDEINPUT_CONTACT_FLAG_INRANGE)
+ {
+ contactState |= TouchContactState_InRange;
+ }
+ if (pContact->u32ContactFlags & VRDEINPUT_CONTACT_FLAG_INCONTACT)
+ {
+ contactState |= TouchContactState_InContact;
+ }
+
+ aContacts[iContact] = RT_MAKE_U64_FROM_U16((uint16_t)x,
+ (uint16_t)y,
+ RT_MAKE_U16(contactId, contactState),
+ 0);
+ }
+
+ if (pFrame->u64FrameOffset == 0)
+ {
+ pThis->mu64TouchInputTimestampMCS = 0;
+ }
+ else
+ {
+ pThis->mu64TouchInputTimestampMCS += pFrame->u64FrameOffset;
+ }
+
+ pMouse->PutEventMultiTouch(pFrame->u16ContactCount,
+ ComSafeArrayAsInParam(aContacts),
+ (ULONG)(pThis->mu64TouchInputTimestampMCS / 1000)); /* Micro->milliseconds. */
+ }
+ }
+ else if (pHeader->u16EventId == VRDEINPUT_EVENTID_DISMISS_HOVERING_CONTACT)
+ {
+ /* @todo */
+ }
+ else
+ {
+ AssertMsgFailed(("EventId %d\n", pHeader->u16EventId));
+ }
+ }
+ }
+}
+
+
void ConsoleVRDPServer::EnableConnections(void)
{
if (mpEntryPoints && mhServer)
{
mpEntryPoints->VRDEEnableConnections(mhServer, true);
- /* Redirect 3D output if it is enabled. */
- remote3DRedirect();
-
/* Setup the generic TSMF channel. */
setupTSMF();
}
@@ -2471,6 +2955,11 @@ void ConsoleVRDPServer::Stop(void)
{
Assert(VALID_PTR(this)); /** @todo r=bird: there are(/was) some odd cases where this buster was invalid on
* linux. Just remove this when it's 100% sure that problem has been fixed. */
+
+#ifdef VBOX_WITH_USB
+ remoteUSBThreadStop();
+#endif /* VBOX_WITH_USB */
+
if (mhServer)
{
HVRDESERVER hServer = mhServer;
@@ -2478,16 +2967,29 @@ void ConsoleVRDPServer::Stop(void)
/* Reset the handle to avoid further calls to the server. */
mhServer = 0;
+ /* Workaround for VM process hangs on termination.
+ *
+ * Make sure that the server is not currently processing a resize.
+ * mhServer 0 will not allow to enter the server again.
+ * Wait until any current resize returns from the server.
+ */
+ if (mcInResize)
+ {
+ LogRel(("VRDP: waiting for resize %d\n", mcInResize));
+
+ int i = 0;
+ while (mcInResize && ++i < 100)
+ {
+ RTThreadSleep(10);
+ }
+ }
+
if (mpEntryPoints && hServer)
{
mpEntryPoints->VRDEDestroy(hServer);
}
}
-#ifdef VBOX_WITH_USB
- remoteUSBThreadStop();
-#endif /* VBOX_WITH_USB */
-
mpfnAuthEntry = NULL;
mpfnAuthEntry2 = NULL;
mpfnAuthEntry3 = NULL;
@@ -2615,6 +3117,77 @@ void ConsoleVRDPServer::remoteUSBThreadStop(void)
}
#endif /* VBOX_WITH_USB */
+typedef struct AuthCtx
+{
+ AuthResult result;
+
+ PAUTHENTRY3 pfnAuthEntry3;
+ PAUTHENTRY2 pfnAuthEntry2;
+ PAUTHENTRY pfnAuthEntry;
+
+ const char *pszCaller;
+ PAUTHUUID pUuid;
+ AuthGuestJudgement guestJudgement;
+ const char *pszUser;
+ const char *pszPassword;
+ const char *pszDomain;
+ int fLogon;
+ unsigned clientId;
+} AuthCtx;
+
+static DECLCALLBACK(int) authThread(RTTHREAD self, void *pvUser)
+{
+ AuthCtx *pCtx = (AuthCtx *)pvUser;
+
+ if (pCtx->pfnAuthEntry3)
+ {
+ pCtx->result = pCtx->pfnAuthEntry3(pCtx->pszCaller, pCtx->pUuid, pCtx->guestJudgement,
+ pCtx->pszUser, pCtx->pszPassword, pCtx->pszDomain,
+ pCtx->fLogon, pCtx->clientId);
+ }
+ else if (pCtx->pfnAuthEntry2)
+ {
+ pCtx->result = pCtx->pfnAuthEntry2(pCtx->pUuid, pCtx->guestJudgement,
+ pCtx->pszUser, pCtx->pszPassword, pCtx->pszDomain,
+ pCtx->fLogon, pCtx->clientId);
+ }
+ else if (pCtx->pfnAuthEntry)
+ {
+ pCtx->result = pCtx->pfnAuthEntry(pCtx->pUuid, pCtx->guestJudgement,
+ pCtx->pszUser, pCtx->pszPassword, pCtx->pszDomain);
+ }
+ return VINF_SUCCESS;
+}
+
+static AuthResult authCall(AuthCtx *pCtx)
+{
+ AuthResult result = AuthResultAccessDenied;
+
+ /* Use a separate thread because external modules might need a lot of stack space. */
+ RTTHREAD thread = NIL_RTTHREAD;
+ int rc = RTThreadCreate(&thread, authThread, pCtx, 512*_1K,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "VRDEAuth");
+ LogFlow(("authCall: RTThreadCreate %Rrc\n", rc));
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadWait(thread, RT_INDEFINITE_WAIT, NULL);
+ LogFlow(("authCall: RTThreadWait %Rrc\n", rc));
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Only update the result if the thread finished without errors. */
+ result = pCtx->result;
+ }
+ else
+ {
+ LogRel(("AUTH: unable to execute the auth thread %Rrc\n", rc));
+ }
+
+ return result;
+}
+
AuthResult ConsoleVRDPServer::Authenticate(const Guid &uuid, AuthGuestJudgement guestJudgement,
const char *pszUser, const char *pszPassword, const char *pszDomain,
uint32_t u32ClientId)
@@ -2638,7 +3211,7 @@ AuthResult ConsoleVRDPServer::Authenticate(const Guid &uuid, AuthGuestJudgement
Utf8Str filename = authLibrary;
- LogRel(("AUTH: ConsoleVRDPServer::Authenticate: loading external authentication library '%ls'\n", authLibrary.raw()));
+ LogRel(("AUTH: loading external authentication library '%ls'\n", authLibrary.raw()));
int rc;
if (RTPathHavePath(filename.c_str()))
@@ -2726,19 +3299,21 @@ AuthResult ConsoleVRDPServer::Authenticate(const Guid &uuid, AuthGuestJudgement
Assert(mAuthLibrary && (mpfnAuthEntry || mpfnAuthEntry2 || mpfnAuthEntry3));
- AuthResult result = AuthResultAccessDenied;
- if (mpfnAuthEntry3)
- {
- result = mpfnAuthEntry3("vrde", &rawuuid, guestJudgement, pszUser, pszPassword, pszDomain, true, u32ClientId);
- }
- else if (mpfnAuthEntry2)
- {
- result = mpfnAuthEntry2(&rawuuid, guestJudgement, pszUser, pszPassword, pszDomain, true, u32ClientId);
- }
- else if (mpfnAuthEntry)
- {
- result = mpfnAuthEntry(&rawuuid, guestJudgement, pszUser, pszPassword, pszDomain);
- }
+ AuthCtx ctx;
+ ctx.result = AuthResultAccessDenied; /* Denied by default. */
+ ctx.pfnAuthEntry3 = mpfnAuthEntry3;
+ ctx.pfnAuthEntry2 = mpfnAuthEntry2;
+ ctx.pfnAuthEntry = mpfnAuthEntry;
+ ctx.pszCaller = "vrde";
+ ctx.pUuid = &rawuuid;
+ ctx.guestJudgement = guestJudgement;
+ ctx.pszUser = pszUser;
+ ctx.pszPassword = pszPassword;
+ ctx.pszDomain = pszDomain;
+ ctx.fLogon = true;
+ ctx.clientId = u32ClientId;
+
+ AuthResult result = authCall(&ctx);
switch (result)
{
@@ -2772,10 +3347,21 @@ void ConsoleVRDPServer::AuthDisconnect(const Guid &uuid, uint32_t u32ClientId)
Assert(mAuthLibrary && (mpfnAuthEntry || mpfnAuthEntry2 || mpfnAuthEntry3));
- if (mpfnAuthEntry3)
- mpfnAuthEntry3("vrde", &rawuuid, AuthGuestNotAsked, NULL, NULL, NULL, false, u32ClientId);
- else if (mpfnAuthEntry2)
- mpfnAuthEntry2(&rawuuid, AuthGuestNotAsked, NULL, NULL, NULL, false, u32ClientId);
+ AuthCtx ctx;
+ ctx.result = AuthResultAccessDenied; /* Not used. */
+ ctx.pfnAuthEntry3 = mpfnAuthEntry3;
+ ctx.pfnAuthEntry2 = mpfnAuthEntry2;
+ ctx.pfnAuthEntry = NULL; /* Does not use disconnect notification. */
+ ctx.pszCaller = "vrde";
+ ctx.pUuid = &rawuuid;
+ ctx.guestJudgement = AuthGuestNotAsked;
+ ctx.pszUser = NULL;
+ ctx.pszPassword = NULL;
+ ctx.pszDomain = NULL;
+ ctx.fLogon = false;
+ ctx.clientId = u32ClientId;
+
+ authCall(&ctx);
}
int ConsoleVRDPServer::lockConsoleVRDPServer(void)
@@ -3210,11 +3796,13 @@ void ConsoleVRDPServer::SendUpdate(unsigned uScreenId, void *pvUpdate, uint32_t
}
}
-void ConsoleVRDPServer::SendResize(void) const
+void ConsoleVRDPServer::SendResize(void)
{
if (mpEntryPoints && mhServer)
{
+ ++mcInResize;
mpEntryPoints->VRDEResize(mhServer);
+ --mcInResize;
}
}
@@ -3297,24 +3885,6 @@ void ConsoleVRDPServer::SendAudioInputEnd(void *pvUserCtx)
}
}
-#ifdef VBOX_WITH_USB_VIDEO
-int ConsoleVRDPServer::GetVideoFrameDimensions(uint16_t *pu16Heigh, uint16_t *pu16Width)
-{
- *pu16Heigh = 640;
- *pu16Width = 480;
- return VINF_SUCCESS;
-}
-
-int ConsoleVRDPServer::SendVideoSreamOn(bool fFetch)
-{
- /* Here we inform server that guest is starting/stopping
- * the stream
- */
- return VINF_SUCCESS;
-}
-#endif
-
-
void ConsoleVRDPServer::QueryInfo(uint32_t index, void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut) const
{
diff --git a/src/VBox/Main/src-client/DisplayImpl.cpp b/src/VBox/Main/src-client/DisplayImpl.cpp
index fc261019..3477a07c 100644
--- a/src/VBox/Main/src-client/DisplayImpl.cpp
+++ b/src/VBox/Main/src-client/DisplayImpl.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;
@@ -30,10 +30,11 @@
#include <iprt/semaphore.h>
#include <iprt/thread.h>
#include <iprt/asm.h>
+#include <iprt/time.h>
#include <iprt/cpp/utils.h>
#include <VBox/vmm/pdmdrv.h>
-#ifdef DEBUG /* for VM_ASSERT_EMT(). */
+#if defined(DEBUG) || defined(VBOX_STRICT) /* for VM_ASSERT_EMT(). */
# include <VBox/vmm/vm.h>
#endif
@@ -48,9 +49,18 @@
#include <VBox/com/array.h>
#ifdef VBOX_WITH_VPX
+# include <iprt/path.h>
# include "VideoRec.h"
#endif
+#ifdef VBOX_WITH_CROGL
+typedef enum
+{
+ CRVREC_STATE_IDLE,
+ CRVREC_STATE_SUBMITTED
+} CRVREC_STATE;
+#endif
+
/**
* Display driver instance data.
*
@@ -76,8 +86,8 @@ typedef struct DRVMAINDISPLAY
#define PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface) RT_FROM_MEMBER(pInterface, DRVMAINDISPLAY, IConnector)
#ifdef DEBUG_sunlover
-static STAMPROFILE StatDisplayRefresh;
-static int stam = 0;
+static STAMPROFILE g_StatDisplayRefresh;
+static int g_stam = 0;
#endif /* DEBUG_sunlover */
// constructor / destructor
@@ -105,6 +115,9 @@ HRESULT Display::FinalConstruct()
mfPendingVideoAccelEnable = false;
mfMachineRunning = false;
+#ifdef VBOX_WITH_CROGL
+ mfCrOglDataHidden = false;
+#endif
mpu8VbvaPartial = NULL;
mcbVbvaPartial = 0;
@@ -121,11 +134,34 @@ HRESULT Display::FinalConstruct()
int rc = RTCritSectInit(&mVBVALock);
AssertRC(rc);
+
+ rc = RTCritSectInit(&mSaveSeamlessRectLock);
+ AssertRC(rc);
+
mfu32PendingVideoAccelDisable = false;
#ifdef VBOX_WITH_HGSMI
mu32UpdateVBVAFlags = 0;
#endif
+#ifdef VBOX_WITH_VPX
+ mpVideoRecCtx = NULL;
+ for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++)
+ maVideoRecEnabled[i] = true;
+#endif
+
+#ifdef VBOX_WITH_CRHGSMI
+ mhCrOglSvc = NULL;
+#endif
+#ifdef VBOX_WITH_CROGL
+ RT_ZERO(mCrOglCallbacks);
+ RT_ZERO(mCrOglScreenshotData);
+ mfCrOglVideoRecState = CRVREC_STATE_IDLE;
+ mCrOglScreenshotData.u32Screen = CRSCREEN_ALL;
+ mCrOglScreenshotData.pvContext = this;
+ mCrOglScreenshotData.pfnScreenshotBegin = displayCrVRecScreenshotBegin;
+ mCrOglScreenshotData.pfnScreenshotPerform = displayCrVRecScreenshotPerform;
+ mCrOglScreenshotData.pfnScreenshotEnd = displayCrVRecScreenshotEnd;
+#endif
return BaseFinalConstruct();
}
@@ -137,7 +173,13 @@ void Display::FinalRelease()
if (RTCritSectIsInitialized (&mVBVALock))
{
RTCritSectDelete (&mVBVALock);
- memset (&mVBVALock, 0, sizeof (mVBVALock));
+ RT_ZERO(mVBVALock);
+ }
+
+ if (RTCritSectIsInitialized(&mSaveSeamlessRectLock))
+ {
+ RTCritSectDelete(&mSaveSeamlessRectLock);
+ RT_ZERO(mSaveSeamlessRectLock);
}
BaseFinalRelease();
}
@@ -205,6 +247,47 @@ static int displayMakeThumbnail(uint8_t *pu8Data, uint32_t cx, uint32_t cy,
return rc;
}
+#ifdef VBOX_WITH_CROGL
+typedef struct
+{
+ CRVBOXHGCMTAKESCREENSHOT Base;
+
+ /* 32bpp small RGB image. */
+ uint8_t *pu8Thumbnail;
+ uint32_t cbThumbnail;
+ uint32_t cxThumbnail;
+ uint32_t cyThumbnail;
+
+ /* PNG screenshot. */
+ uint8_t *pu8PNG;
+ uint32_t cbPNG;
+ uint32_t cxPNG;
+ uint32_t cyPNG;
+} VBOX_DISPLAY_SAVESCREENSHOT_DATA;
+
+static DECLCALLBACK(void) displaySaveScreenshotReport(void *pvCtx, uint32_t uScreen,
+ uint32_t x, uint32_t y, uint32_t uBitsPerPixel,
+ uint32_t uBytesPerLine, uint32_t uGuestWidth, uint32_t uGuestHeight,
+ uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
+{
+ VBOX_DISPLAY_SAVESCREENSHOT_DATA *pData = (VBOX_DISPLAY_SAVESCREENSHOT_DATA*)pvCtx;
+ displayMakeThumbnail(pu8BufferAddress, uGuestWidth, uGuestHeight, &pData->pu8Thumbnail, &pData->cbThumbnail, &pData->cxThumbnail, &pData->cyThumbnail);
+ int rc = DisplayMakePNG(pu8BufferAddress, uGuestWidth, uGuestHeight, &pData->pu8PNG, &pData->cbPNG, &pData->cxPNG, &pData->cyPNG, 1);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("DisplayMakePNG failed %d\n", rc));
+ if (pData->pu8PNG)
+ {
+ RTMemFree(pData->pu8PNG);
+ pData->pu8PNG = NULL;
+ }
+ pData->cbPNG = 0;
+ pData->cxPNG = 0;
+ pData->cyPNG = 0;
+ }
+}
+#endif
+
DECLCALLBACK(void)
Display::displaySSMSaveScreenshot(PSSMHANDLE pSSM, void *pvUser)
{
@@ -222,8 +305,8 @@ Display::displaySSMSaveScreenshot(PSSMHANDLE pSSM, void *pvUser)
uint32_t cxPNG = 0;
uint32_t cyPNG = 0;
- Console::SafeVMPtr pVM (that->mParent);
- if (SUCCEEDED(pVM.rc()))
+ Console::SafeVMPtr ptrVM(that->mParent);
+ if (ptrVM.isOk())
{
/* Query RGB bitmap. */
uint8_t *pu8Data = NULL;
@@ -231,39 +314,101 @@ Display::displaySSMSaveScreenshot(PSSMHANDLE pSSM, void *pvUser)
uint32_t cx = 0;
uint32_t cy = 0;
- /* SSM code is executed on EMT(0), therefore no need to use VMR3ReqCallWait. */
- int rc = Display::displayTakeScreenshotEMT(that, VBOX_VIDEO_PRIMARY_SCREEN, &pu8Data, &cbData, &cx, &cy);
+#ifdef VBOX_WITH_CROGL
+ BOOL f3DSnapshot = FALSE;
+ BOOL is3denabled;
+ that->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
+ if (is3denabled && that->mCrOglCallbacks.pfnHasData())
+ {
+ VMMDev *pVMMDev = that->mParent->getVMMDev();
+ if (pVMMDev)
+ {
+ VBOX_DISPLAY_SAVESCREENSHOT_DATA *pScreenshot = (VBOX_DISPLAY_SAVESCREENSHOT_DATA*)RTMemAllocZ(sizeof (*pScreenshot));
+ if (pScreenshot)
+ {
+ /* screen id or CRSCREEN_ALL to specify all enabled */
+ pScreenshot->Base.u32Screen = 0;
+ pScreenshot->Base.u32Width = 0;
+ pScreenshot->Base.u32Height = 0;
+ pScreenshot->Base.u32Pitch = 0;
+ pScreenshot->Base.pvBuffer = NULL;
+ pScreenshot->Base.pvContext = pScreenshot;
+ pScreenshot->Base.pfnScreenshotBegin = NULL;
+ pScreenshot->Base.pfnScreenshotPerform = displaySaveScreenshotReport;
+ pScreenshot->Base.pfnScreenshotEnd = NULL;
+
+ VBOXHGCMSVCPARM parm;
+
+ parm.type = VBOX_HGCM_SVC_PARM_PTR;
+ parm.u.pointer.addr = &pScreenshot->Base;
+ parm.u.pointer.size = sizeof (pScreenshot->Base);
+
+ int rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_TAKE_SCREENSHOT, 1, &parm);
+ if (RT_SUCCESS(rc))
+ {
+ if (pScreenshot->pu8PNG)
+ {
+ pu8Thumbnail = pScreenshot->pu8Thumbnail;
+ cbThumbnail = pScreenshot->cbThumbnail;
+ cxThumbnail = pScreenshot->cxThumbnail;
+ cyThumbnail = pScreenshot->cyThumbnail;
+
+ /* PNG screenshot. */
+ pu8PNG = pScreenshot->pu8PNG;
+ cbPNG = pScreenshot->cbPNG;
+ cxPNG = pScreenshot->cxPNG;
+ cyPNG = pScreenshot->cyPNG;
+ f3DSnapshot = TRUE;
+ }
+ else
+ AssertMsgFailed(("no png\n"));
+ }
+ else
+ AssertMsgFailed(("SHCRGL_HOST_FN_TAKE_SCREENSHOT failed %d\n", rc));
- /*
- * It is possible that success is returned but everything is 0 or NULL.
- * (no display attached if a VM is running with VBoxHeadless on OSE for example)
- */
- if (RT_SUCCESS(rc) && pu8Data)
+
+ RTMemFree(pScreenshot);
+ }
+ }
+ }
+
+ if (!f3DSnapshot)
+#endif
{
- Assert(cx && cy);
+ /* SSM code is executed on EMT(0), therefore no need to use VMR3ReqCallWait. */
+ int rc = Display::displayTakeScreenshotEMT(that, VBOX_VIDEO_PRIMARY_SCREEN, &pu8Data, &cbData, &cx, &cy);
- /* Prepare a small thumbnail and a PNG screenshot. */
- displayMakeThumbnail(pu8Data, cx, cy, &pu8Thumbnail, &cbThumbnail, &cxThumbnail, &cyThumbnail);
- rc = DisplayMakePNG(pu8Data, cx, cy, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 1);
- if (RT_FAILURE(rc))
+ /*
+ * It is possible that success is returned but everything is 0 or NULL.
+ * (no display attached if a VM is running with VBoxHeadless on OSE for example)
+ */
+ if (RT_SUCCESS(rc) && pu8Data)
{
- if (pu8PNG)
+ Assert(cx && cy);
+
+ /* Prepare a small thumbnail and a PNG screenshot. */
+ displayMakeThumbnail(pu8Data, cx, cy, &pu8Thumbnail, &cbThumbnail, &cxThumbnail, &cyThumbnail);
+ rc = DisplayMakePNG(pu8Data, cx, cy, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 1);
+ if (RT_FAILURE(rc))
{
- RTMemFree(pu8PNG);
- pu8PNG = NULL;
+ if (pu8PNG)
+ {
+ RTMemFree(pu8PNG);
+ pu8PNG = NULL;
+ }
+ cbPNG = 0;
+ cxPNG = 0;
+ cyPNG = 0;
}
- cbPNG = 0;
- cxPNG = 0;
- cyPNG = 0;
- }
- /* This can be called from any thread. */
- that->mpDrv->pUpPort->pfnFreeScreenshot(that->mpDrv->pUpPort, pu8Data);
+ /* This can be called from any thread. */
+ that->mpDrv->pUpPort->pfnFreeScreenshot(that->mpDrv->pUpPort, pu8Data);
+ }
}
}
else
{
- LogFunc(("Failed to get VM pointer 0x%x\n", pVM.rc()));
+ LogFunc(("Failed to get VM pointer 0x%x\n", ptrVM.rc()));
}
/* Regardless of rc, save what is available:
@@ -436,40 +581,8 @@ HRESULT Display::init(Console *aParent)
unconst(mParent) = aParent;
- // by default, we have an internal framebuffer which is
- // NULL, i.e. a black hole for no display output
- mFramebufferOpened = false;
-
ULONG ul;
mParent->machine()->COMGETTER(MonitorCount)(&ul);
-
-#ifdef VBOX_WITH_VPX
- if (VideoRecContextCreate(&mpVideoRecContext))
- {
- LogFlow(("Failed to create Video Recording Context\n"));
- return E_FAIL;
- }
-
- BOOL fEnabled = false;
- mParent->machine()->COMGETTER(VideoCaptureEnabled)(&fEnabled);
- if (fEnabled)
- {
- ULONG ulVideoCaptureHorzRes;
- mParent->machine()->COMGETTER(VideoCaptureWidth)(&ulVideoCaptureHorzRes);
- ULONG ulVideoCaptureVertRes;
- mParent->machine()->COMGETTER(VideoCaptureHeight)(&ulVideoCaptureVertRes);
- BSTR strVideoCaptureFile;
- mParent->machine()->COMGETTER(VideoCaptureFile)(&strVideoCaptureFile);
- LogFlow(("VidoeRecording VPX enabled\n"));
- if (VideoRecContextInit(mpVideoRecContext, strVideoCaptureFile,
- ulVideoCaptureHorzRes, ulVideoCaptureVertRes))
- {
- LogFlow(("Failed to initialize video recording context\n"));
- return E_FAIL;
- }
- }
-#endif
-
mcMonitors = ul;
for (ul = 0; ul < mcMonitors; ul++)
@@ -500,14 +613,20 @@ HRESULT Display::init(Console *aParent)
maFramebuffers[ul].fDefaultFormat = false;
- memset (&maFramebuffers[ul].dirtyRect, 0 , sizeof (maFramebuffers[ul].dirtyRect));
- memset (&maFramebuffers[ul].pendingResize, 0 , sizeof (maFramebuffers[ul].pendingResize));
+ maFramebuffers[ul].mcSavedVisibleRegion = 0;
+ maFramebuffers[ul].mpSavedVisibleRegion = NULL;
+
+ RT_ZERO(maFramebuffers[ul].dirtyRect);
+ RT_ZERO(maFramebuffers[ul].pendingResize);
#ifdef VBOX_WITH_HGSMI
maFramebuffers[ul].fVBVAEnabled = false;
maFramebuffers[ul].cVBVASkipUpdate = 0;
- memset (&maFramebuffers[ul].vbvaSkippedRect, 0, sizeof (maFramebuffers[ul].vbvaSkippedRect));
+ RT_ZERO(maFramebuffers[ul].vbvaSkippedRect);
maFramebuffers[ul].pVBVAHostFlags = NULL;
#endif /* VBOX_WITH_HGSMI */
+#ifdef VBOX_WITH_CROGL
+ RT_ZERO(maFramebuffers[ul].pendingViewportInfo);
+#endif
}
{
@@ -557,23 +676,18 @@ void Display::uninit()
mpDrv = NULL;
mpVMMDev = NULL;
mfVMMDevInited = true;
-
-#ifdef VBOX_WITH_VPX
- if (mpVideoRecContext)
- VideoRecContextClose(mpVideoRecContext);
-#endif
}
/**
* Register the SSM methods. Called by the power up thread to be able to
* pass pVM
*/
-int Display::registerSSM(PVM pVM)
+int Display::registerSSM(PUVM pUVM)
{
/* Version 2 adds width and height of the framebuffer; version 3 adds
* the framebuffer offset in the virtual desktop and the framebuffer flags.
*/
- int rc = SSMR3RegisterExternal(pVM, "DisplayData", 0, sSSMDisplayVer3,
+ int rc = SSMR3RegisterExternal(pUVM, "DisplayData", 0, sSSMDisplayVer3,
mcMonitors * sizeof(uint32_t) * 8 + sizeof(uint32_t),
NULL, NULL, NULL,
NULL, displaySSMSave, NULL,
@@ -584,20 +698,20 @@ int Display::registerSSM(PVM pVM)
* Register loaders for old saved states where iInstance was
* 3 * sizeof(uint32_t *) due to a code mistake.
*/
- rc = SSMR3RegisterExternal(pVM, "DisplayData", 12 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
+ rc = SSMR3RegisterExternal(pUVM, "DisplayData", 12 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, displaySSMLoad, NULL, this);
AssertRCReturn(rc, rc);
- rc = SSMR3RegisterExternal(pVM, "DisplayData", 24 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
+ rc = SSMR3RegisterExternal(pUVM, "DisplayData", 24 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, displaySSMLoad, NULL, this);
AssertRCReturn(rc, rc);
/* uInstance is an arbitrary value greater than 1024. Such a value will ensure a quick seek in saved state file. */
- rc = SSMR3RegisterExternal(pVM, "DisplayScreenshot", 1100 /*uInstance*/, sSSMDisplayScreenshotVer, 0 /*cbGuess*/,
+ rc = SSMR3RegisterExternal(pUVM, "DisplayScreenshot", 1100 /*uInstance*/, sSSMDisplayScreenshotVer, 0 /*cbGuess*/,
NULL, NULL, NULL,
NULL, displaySSMSaveScreenshot, NULL,
NULL, displaySSMLoadScreenshot, NULL, this);
@@ -607,6 +721,46 @@ int Display::registerSSM(PVM pVM)
return VINF_SUCCESS;
}
+#ifdef VBOX_WITH_CROGL
+int Display::crOglWindowsShow(bool fShow)
+{
+ if (!mfCrOglDataHidden == !!fShow)
+ return VINF_SUCCESS;
+
+ if (!mhCrOglSvc)
+ {
+ /* no 3D */
+#ifdef DEBUG
+ BOOL is3denabled;
+ mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
+ Assert(!is3denabled);
+#endif
+ return VERR_INVALID_STATE;
+ }
+
+ VMMDev *pVMMDev = mParent->getVMMDev();
+ if (!pVMMDev)
+ {
+ AssertMsgFailed(("no vmmdev\n"));
+ return VERR_INVALID_STATE;
+ }
+
+ VBOXHGCMSVCPARM parm;
+
+ parm.type = VBOX_HGCM_SVC_PARM_32BIT;
+ parm.u.uint32 = (uint32_t)fShow;
+
+ int rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_WINDOWS_SHOW, &parm, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ mfCrOglDataHidden = !fShow;
+ else
+ AssertMsgFailed(("hgcmHostFastCallAsync failed rc %n", rc));
+
+ return rc;
+}
+#endif
+
+
// IEventListener method
STDMETHODIMP Display::HandleEvent(IEvent * aEvent)
{
@@ -629,9 +783,20 @@ STDMETHODIMP Display::HandleEvent(IEvent * aEvent)
LogRelFlowFunc(("Machine is running.\n"));
mfMachineRunning = true;
+
+#ifdef VBOX_WITH_CROGL
+ crOglWindowsShow(true);
+#endif
}
else
+ {
mfMachineRunning = false;
+
+#ifdef VBOX_WITH_CROGL
+ if (machineState == MachineState_Paused)
+ crOglWindowsShow(false);
+#endif
+ }
break;
}
default:
@@ -650,7 +815,7 @@ STDMETHODIMP Display::HandleEvent(IEvent * aEvent)
static int callFramebufferResize (IFramebuffer *pFramebuffer, unsigned uScreenId,
ULONG pixelFormat, void *pvVRAM,
uint32_t bpp, uint32_t cbLine,
- int w, int h)
+ uint32_t w, uint32_t h)
{
Assert (pFramebuffer);
@@ -662,13 +827,53 @@ static int callFramebufferResize (IFramebuffer *pFramebuffer, unsigned uScreenId
if (!finished)
{
- LogRelFlowFunc (("External framebuffer wants us to wait!\n"));
+ LogRelFlowFunc(("External framebuffer wants us to wait!\n"));
return VINF_VGA_RESIZE_IN_PROGRESS;
}
return VINF_SUCCESS;
}
+int Display::notifyCroglResize(const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, void *pvVRAM)
+{
+#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
+ BOOL is3denabled;
+ mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
+
+ if (is3denabled)
+ {
+ int rc = VERR_INVALID_STATE;
+ if (mhCrOglSvc)
+ {
+ VMMDev *pVMMDev = mParent->getVMMDev();
+ if (pVMMDev)
+ {
+ CRVBOXHGCMDEVRESIZE *pData = (CRVBOXHGCMDEVRESIZE*)RTMemAlloc(sizeof (*pData));
+ if (pData)
+ {
+ pData->Screen = *pScreen;
+ pData->pvVRAM = pvVRAM;
+
+ VBOXHGCMSVCPARM parm;
+
+ parm.type = VBOX_HGCM_SVC_PARM_PTR;
+ parm.u.pointer.addr = pData;
+ parm.u.pointer.size = sizeof (*pData);
+
+ rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_DEV_RESIZE, &parm, displayCrAsyncCmdCompletion, this);
+ AssertRC(rc);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ return rc;
+ }
+#endif /* #if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL) */
+ return VINF_SUCCESS;
+}
+
/**
* Handles display resize event.
* Disables access to VGA device;
@@ -682,11 +887,11 @@ static int callFramebufferResize (IFramebuffer *pFramebuffer, unsigned uScreenId
* @thread EMT
*/
int Display::handleDisplayResize (unsigned uScreenId, uint32_t bpp, void *pvVRAM,
- uint32_t cbLine, int w, int h, uint16_t flags)
+ uint32_t cbLine, uint32_t w, uint32_t h, uint16_t flags)
{
- LogRel (("Display::handleDisplayResize(): uScreenId = %d, pvVRAM=%p "
- "w=%d h=%d bpp=%d cbLine=0x%X, flags=0x%X\n",
- uScreenId, pvVRAM, w, h, bpp, cbLine, flags));
+ LogRel(("Display::handleDisplayResize(): uScreenId = %d, pvVRAM=%p "
+ "w=%d h=%d bpp=%d cbLine=0x%X, flags=0x%X\n",
+ uScreenId, pvVRAM, w, h, bpp, cbLine, flags));
/* If there is no framebuffer, this call is not interesting. */
if ( uScreenId >= mcMonitors
@@ -695,12 +900,15 @@ int Display::handleDisplayResize (unsigned uScreenId, uint32_t bpp, void *pvVRAM
return VINF_SUCCESS;
}
- mLastAddress = pvVRAM;
- mLastBytesPerLine = cbLine;
- mLastBitsPerPixel = bpp,
- mLastWidth = w;
- mLastHeight = h;
- mLastFlags = flags;
+ if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
+ {
+ mLastAddress = pvVRAM;
+ mLastBytesPerLine = cbLine;
+ mLastBitsPerPixel = bpp;
+ mLastWidth = w;
+ mLastHeight = h;
+ mLastFlags = flags;
+ }
ULONG pixelFormat;
@@ -732,7 +940,7 @@ int Display::handleDisplayResize (unsigned uScreenId, uint32_t bpp, void *pvVRAM
*
* Note: the resize information is only accessed on EMT so no serialization is required.
*/
- LogRel (("Display::handleDisplayResize(): Warning: resize postponed.\n"));
+ LogRel(("Display::handleDisplayResize(): Warning: resize postponed.\n"));
maFramebuffers[uScreenId].pendingResize.fPending = true;
maFramebuffers[uScreenId].pendingResize.pixelFormat = pixelFormat;
@@ -746,6 +954,10 @@ int Display::handleDisplayResize (unsigned uScreenId, uint32_t bpp, void *pvVRAM
return VINF_VGA_RESIZE_IN_PROGRESS;
}
+ /* Framebuffer will be invalid during resize, make sure that it is not accessed. */
+ if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
+ mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort, false);
+
int rc = callFramebufferResize (maFramebuffers[uScreenId].pFramebuffer, uScreenId,
pixelFormat, pvVRAM, bpp, cbLine, w, h);
if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
@@ -810,11 +1022,17 @@ void Display::handleResizeCompletedEMT (void)
continue;
}
+ /* Inform VRDP server about the change of display parameters.
+ * Must be done before calling NotifyUpdate below.
+ */
+ LogRelFlowFunc(("Calling VRDP\n"));
+ mParent->consoleVRDPServer()->SendResize();
+
/* @todo Merge these two 'if's within one 'if (!pFBInfo->pFramebuffer.isNull())' */
if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && !pFBInfo->pFramebuffer.isNull())
{
/* Primary framebuffer has completed the resize. Update the connector data for VGA device. */
- updateDisplayData();
+ int rc2 = updateDisplayData();
/* Check the framebuffer pixel format to setup the rendering in VGA device. */
BOOL usesGuestVRAM = FALSE;
@@ -825,7 +1043,7 @@ void Display::handleResizeCompletedEMT (void)
/* If the primary framebuffer is disabled, tell the VGA device to not to copy
* pixels from VRAM to the framebuffer.
*/
- if (pFBInfo->fDisabled)
+ if (pFBInfo->fDisabled || RT_FAILURE(rc2))
mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort, false);
else
mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort,
@@ -854,22 +1072,33 @@ void Display::handleResizeCompletedEMT (void)
}
LogRelFlow(("[%d]: default format %d\n", uScreenId, pFBInfo->fDefaultFormat));
-#ifdef DEBUG_sunlover
- if (!stam)
+ /* Handle the case if there are some saved visible region that needs to be
+ * applied after the resize of the framebuffer is completed
+ */
+ SaveSeamlessRectLock();
+ PRTRECT pSavedVisibleRegion = pFBInfo->mpSavedVisibleRegion;
+ uint32_t cSavedVisibleRegion = pFBInfo->mcSavedVisibleRegion;
+ pFBInfo->mpSavedVisibleRegion = NULL;
+ pFBInfo->mcSavedVisibleRegion = 0;
+ SaveSeamlessRectUnLock();
+
+ if (pSavedVisibleRegion)
{
- /* protect mpVM */
- Console::SafeVMPtr pVM (mParent);
- AssertComRC (pVM.rc());
+ handleSetVisibleRegion(cSavedVisibleRegion, pSavedVisibleRegion);
+ RTMemFree(pSavedVisibleRegion);
+ }
- STAM_REG(pVM, &StatDisplayRefresh, STAMTYPE_PROFILE, "/PROF/Display/Refresh", STAMUNIT_TICKS_PER_CALL, "Time spent in EMT for display updates.");
- stam = 1;
+#ifdef DEBUG_sunlover
+ if (!g_stam)
+ {
+ Console::SafeVMPtr ptrVM(mParent);
+ AssertComRC(ptrVM.rc());
+ STAMR3RegisterU(ptrVM.rawUVM(), &g_StatDisplayRefresh, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
+ "/PROF/Display/Refresh", STAMUNIT_TICKS_PER_CALL, "Time spent in EMT for display updates.");
+ g_stam = 1;
}
#endif /* DEBUG_sunlover */
- /* Inform VRDP server about the change of display parameters. */
- LogRelFlowFunc (("Calling VRDP\n"));
- mParent->consoleVRDPServer()->SendResize();
-
#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
{
BOOL is3denabled;
@@ -884,7 +1113,16 @@ void Display::handleResizeCompletedEMT (void)
VMMDev *pVMMDev = mParent->getVMMDev();
if (pVMMDev)
+ {
+#if 0
+ if (mhCrOglSvc)
+ pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_SCREEN_CHANGED, &parm, NULL, NULL);
+ else
+ AssertMsgFailed(("mhCrOglSvc is NULL\n"));
+#else
pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_SCREEN_CHANGED, SHCRGL_CPARMS_SCREEN_CHANGED, &parm);
+#endif
+ }
}
}
#endif /* VBOX_WITH_CROGL */
@@ -928,17 +1166,17 @@ unsigned mapCoordsToScreen(DISPLAYFBINFO *pInfos, unsigned cInfos, int *px, int
{
DISPLAYFBINFO *pInfo = pInfos;
unsigned uScreenId;
- LogSunlover (("mapCoordsToScreen: %d,%d %dx%d\n", *px, *py, *pw, *ph));
+ LogSunlover(("mapCoordsToScreen: %d,%d %dx%d\n", *px, *py, *pw, *ph));
for (uScreenId = 0; uScreenId < cInfos; uScreenId++, pInfo++)
{
- LogSunlover ((" [%d] %d,%d %dx%d\n", uScreenId, pInfo->xOrigin, pInfo->yOrigin, pInfo->w, pInfo->h));
+ LogSunlover((" [%d] %d,%d %dx%d\n", uScreenId, pInfo->xOrigin, pInfo->yOrigin, pInfo->w, pInfo->h));
if ( (pInfo->xOrigin <= *px && *px < pInfo->xOrigin + (int)pInfo->w)
&& (pInfo->yOrigin <= *py && *py < pInfo->yOrigin + (int)pInfo->h))
{
/* The rectangle belongs to the screen. Correct coordinates. */
*px -= pInfo->xOrigin;
*py -= pInfo->yOrigin;
- LogSunlover ((" -> %d,%d", *px, *py));
+ LogSunlover((" -> %d,%d", *px, *py));
break;
}
}
@@ -947,7 +1185,7 @@ unsigned mapCoordsToScreen(DISPLAYFBINFO *pInfos, unsigned cInfos, int *px, int
/* Map to primary screen. */
uScreenId = 0;
}
- LogSunlover ((" scr %d\n", uScreenId));
+ LogSunlover((" scr %d\n", uScreenId));
return uScreenId;
}
@@ -967,7 +1205,7 @@ void Display::handleDisplayUpdateLegacy (int x, int y, int w, int h)
unsigned uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
#ifdef DEBUG_sunlover
- LogFlowFunc (("%d,%d %dx%d (checked)\n", x, y, w, h));
+ LogFlowFunc(("%d,%d %dx%d (checked)\n", x, y, w, h));
#endif /* DEBUG_sunlover */
handleDisplayUpdate (uScreenId, x, y, w, h);
@@ -981,8 +1219,8 @@ void Display::handleDisplayUpdate (unsigned uScreenId, int x, int y, int w, int
*/
#ifdef DEBUG_sunlover
- LogFlowFunc (("[%d] %d,%d %dx%d (%d,%d)\n",
- uScreenId, x, y, w, h, mpDrv->IConnector.cx, mpDrv->IConnector.cy));
+ LogFlowFunc(("[%d] %d,%d %dx%d (%d,%d)\n",
+ uScreenId, x, y, w, h, mpDrv->IConnector.cx, mpDrv->IConnector.cy));
#endif /* DEBUG_sunlover */
IFramebuffer *pFramebuffer = maFramebuffers[uScreenId].pFramebuffer;
@@ -1041,7 +1279,7 @@ void Display::getFramebufferDimensions(int32_t *px1, int32_t *py1,
return;
/* If VBVA is not in use then this flag will not be set and this
* will still work as it should. */
- if (!(maFramebuffers[0].fDisabled))
+ if (!maFramebuffers[0].fDisabled)
{
x1 = (int32_t)maFramebuffers[0].xOrigin;
y1 = (int32_t)maFramebuffers[0].yOrigin;
@@ -1050,7 +1288,7 @@ void Display::getFramebufferDimensions(int32_t *px1, int32_t *py1,
}
for (unsigned i = 1; i < mcMonitors; ++i)
{
- if (!(maFramebuffers[i].fDisabled))
+ if (!maFramebuffers[i].fDisabled)
{
x1 = RT_MIN(x1, maFramebuffers[i].xOrigin);
y1 = RT_MIN(y1, maFramebuffers[i].yOrigin);
@@ -1115,6 +1353,38 @@ int Display::handleSetVisibleRegion(uint32_t cRect, PRTRECT pRect)
if (!pFBInfo->pFramebuffer.isNull())
{
+ if (pFBInfo->u32ResizeStatus != ResizeStatus_Void)
+ {
+ /* handle the case where new rectangles are received from the GA
+ * when framebuffer resizing is in progress.
+ * Just save the rectangles to be applied for later time when FB resizing is complete
+ * (from handleResizeCompletedEMT).
+ * This is done to prevent a race condition where a new rectangles are received
+ * from the GA after a resize event and framebuffer resizing is still in progress
+ * As a result the coordinates of the framebuffer are still
+ * not updated and hence there is no intersection with the new rectangles passed
+ * for the new region (THis is checked in the above if condition ). With 0 intersection,
+ * cRectVisibleRegions = 0 is returned to the GUI and if GUI has invalidated its
+ * earlier region then it draws nothihing and seamless mode doesn't display the
+ * guest desktop.
+ */
+ SaveSeamlessRectLock();
+ RTMemFree(pFBInfo->mpSavedVisibleRegion);
+
+ pFBInfo->mpSavedVisibleRegion = (RTRECT *)RTMemAlloc( RT_MAX(cRect, 1)
+ * sizeof (RTRECT));
+ if (pFBInfo->mpSavedVisibleRegion)
+ {
+ memcpy(pFBInfo->mpSavedVisibleRegion, pRect, cRect * sizeof(RTRECT));
+ pFBInfo->mcSavedVisibleRegion = cRect;
+ }
+ else
+ {
+ pFBInfo->mcSavedVisibleRegion = 0;
+ }
+ SaveSeamlessRectUnLock();
+ continue;
+ }
/* Prepare a new array of rectangles which intersect with the framebuffer.
*/
RTRECT rectFramebuffer;
@@ -1156,13 +1426,11 @@ int Display::handleSetVisibleRegion(uint32_t cRect, PRTRECT pRect)
cRectVisibleRegion++;
}
}
-
pFBInfo->pFramebuffer->SetVisibleRegion((BYTE *)pVisibleRegion, cRectVisibleRegion);
}
}
-#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
- // @todo fix for multimonitor
+#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
BOOL is3denabled = FALSE;
mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
@@ -1170,15 +1438,27 @@ int Display::handleSetVisibleRegion(uint32_t cRect, PRTRECT pRect)
VMMDev *vmmDev = mParent->getVMMDev();
if (is3denabled && vmmDev)
{
- VBOXHGCMSVCPARM parms[2];
+ if (mhCrOglSvc)
+ {
+ RTRECT *pRectsCopy = (RTRECT *)RTMemAlloc( RT_MAX(cRect, 1)
+ * sizeof (RTRECT));
+ if (pRectsCopy)
+ {
+ memcpy(pRectsCopy, pRect, cRect * sizeof (RTRECT));
+
+ VBOXHGCMSVCPARM parm;
- parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
- parms[0].u.pointer.addr = pRect;
- parms[0].u.pointer.size = 0; /* We don't actually care. */
- parms[1].type = VBOX_HGCM_SVC_PARM_32BIT;
- parms[1].u.uint32 = cRect;
+ parm.type = VBOX_HGCM_SVC_PARM_PTR;
+ parm.u.pointer.addr = pRectsCopy;
+ parm.u.pointer.size = cRect * sizeof (RTRECT);
- vmmDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_SET_VISIBLE_REGION, 2, &parms[0]);
+ vmmDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_SET_VISIBLE_REGION, &parm, displayCrAsyncCmdCompletion, this);
+ }
+ else
+ AssertMsgFailed(("failed to allocate rects memory\n"));
+ }
+ else
+ AssertMsgFailed(("mhCrOglSvc is NULL\n"));
}
#endif
@@ -1215,14 +1495,14 @@ static void vbvaRgnInit (VBVADIRTYREGION *prgn, DISPLAYFBINFO *paFramebuffers, u
{
DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
- memset (&pFBInfo->dirtyRect, 0, sizeof (pFBInfo->dirtyRect));
+ RT_ZERO(pFBInfo->dirtyRect);
}
}
static void vbvaRgnDirtyRect (VBVADIRTYREGION *prgn, unsigned uScreenId, VBVACMDHDR *phdr)
{
- LogSunlover (("x = %d, y = %d, w = %d, h = %d\n",
- phdr->x, phdr->y, phdr->w, phdr->h));
+ LogSunlover(("x = %d, y = %d, w = %d, h = %d\n",
+ phdr->x, phdr->y, phdr->w, phdr->h));
/*
* Here update rectangles are accumulated to form an update area.
@@ -1396,6 +1676,17 @@ void Display::vbvaUnlock(void)
RTCritSectLeave(&mVBVALock);
}
+int Display::SaveSeamlessRectLock(void)
+{
+ return RTCritSectEnter(&mSaveSeamlessRectLock);
+}
+
+void Display::SaveSeamlessRectUnLock(void)
+{
+ RTCritSectLeave(&mSaveSeamlessRectLock);
+}
+
+
/**
* @thread EMT
*/
@@ -1422,7 +1713,7 @@ int Display::videoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
* Guest enabled acceleration at will. And it has to enable
* acceleration after a mode change.
*/
- LogRelFlowFunc (("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
+ LogRelFlowFunc(("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
mfVideoAccelEnabled, fEnable, pVbvaMemory));
/* Strictly check parameters. Callers must not pass anything in the case. */
@@ -1439,7 +1730,7 @@ int Display::videoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
{
Assert (!mfVideoAccelEnabled);
- LogRelFlowFunc (("Machine is not yet running.\n"));
+ LogRelFlowFunc(("Machine is not yet running.\n"));
if (fEnable)
{
@@ -1507,7 +1798,7 @@ int Display::videoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
LogRel(("VBVA: Disabled.\n"));
}
- LogRelFlowFunc (("VideoAccelEnable: rc = %Rrc.\n", rc));
+ LogRelFlowFunc(("VideoAccelEnable: rc = %Rrc.\n", rc));
return rc;
}
@@ -1582,7 +1873,7 @@ static void vbvaFetchBytes (VBVAMEMORY *pVbvaMemory, uint8_t *pu8Dst, uint32_t c
{
if (cbDst >= VBVA_RING_BUFFER_SIZE)
{
- AssertMsgFailed (("cbDst = 0x%08X, ring buffer size 0x%08X", cbDst, VBVA_RING_BUFFER_SIZE));
+ AssertMsgFailed (("cbDst = 0x%08X, ring buffer size 0x%08X\n", cbDst, VBVA_RING_BUFFER_SIZE));
return;
}
@@ -1662,8 +1953,8 @@ bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
uint32_t indexRecordFree = mpVbvaMemory->indexRecordFree;
#ifdef DEBUG_sunlover
- LogFlowFunc (("first = %d, free = %d\n",
- indexRecordFirst, indexRecordFree));
+ LogFlowFunc(("first = %d, free = %d\n",
+ indexRecordFirst, indexRecordFree));
#endif /* DEBUG_sunlover */
if (!vbvaVerifyRingBuffer (mpVbvaMemory))
@@ -1680,7 +1971,7 @@ bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
VBVARECORD *pRecord = &mpVbvaMemory->aRecords[indexRecordFirst];
#ifdef DEBUG_sunlover
- LogFlowFunc (("cbRecord = 0x%08X\n", pRecord->cbRecord));
+ LogFlowFunc(("cbRecord = 0x%08X\n", pRecord->cbRecord));
#endif /* DEBUG_sunlover */
uint32_t cbRecord = pRecord->cbRecord & ~VBVA_F_RECORD_PARTIAL;
@@ -1691,7 +1982,7 @@ bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
Assert (mpu8VbvaPartial);
- LogFlowFunc (("continue partial record mcbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
+ LogFlowFunc(("continue partial record mcbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
if (cbRecord > mcbVbvaPartial)
@@ -1716,7 +2007,7 @@ bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
#ifdef DEBUG_sunlover
- LogFlowFunc (("partial done ok, data = %d, free = %d\n",
+ LogFlowFunc(("partial done ok, data = %d, free = %d\n",
mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
#endif /* DEBUG_sunlover */
}
@@ -1736,7 +2027,7 @@ bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
return false;
}
- LogFlowFunc (("started partial record mcbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
+ LogFlowFunc(("started partial record mcbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
}
@@ -1771,7 +2062,7 @@ bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
if (!dst)
{
- LogRelFlowFunc (("could not allocate %d bytes from heap!!!\n", cbRecord));
+ LogRelFlowFunc(("could not allocate %d bytes from heap!!!\n", cbRecord));
mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
return false;
}
@@ -1781,7 +2072,7 @@ bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
*ppHdr = (VBVACMDHDR *)dst;
#ifdef DEBUG_sunlover
- LogFlowFunc (("Allocated from heap %p\n", dst));
+ LogFlowFunc(("Allocated from heap %p\n", dst));
#endif /* DEBUG_sunlover */
}
}
@@ -1792,8 +2083,8 @@ bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
#ifdef DEBUG_sunlover
- LogFlowFunc (("done ok, data = %d, free = %d\n",
- mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
+ LogFlowFunc(("done ok, data = %d, free = %d\n",
+ mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
#endif /* DEBUG_sunlover */
return true;
@@ -1818,7 +2109,7 @@ void Display::vbvaReleaseCmd (VBVACMDHDR *pHdr, int32_t cbCmd)
/* The pointer is outside. It is then an allocated copy. */
#ifdef DEBUG_sunlover
- LogFlowFunc (("Free heap %p\n", pHdr));
+ LogFlowFunc(("Free heap %p\n", pHdr));
#endif /* DEBUG_sunlover */
if ((uint8_t *)pHdr == mpu8VbvaPartial)
@@ -1855,7 +2146,7 @@ void Display::VideoAccelFlush (void)
void Display::videoAccelFlush (void)
{
#ifdef DEBUG_sunlover_2
- LogFlowFunc (("mfVideoAccelEnabled = %d\n", mfVideoAccelEnabled));
+ LogFlowFunc(("mfVideoAccelEnabled = %d\n", mfVideoAccelEnabled));
#endif /* DEBUG_sunlover_2 */
if (!mfVideoAccelEnabled)
@@ -1868,7 +2159,7 @@ void Display::videoAccelFlush (void)
Assert(mpVbvaMemory);
#ifdef DEBUG_sunlover_2
- LogFlowFunc (("indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
+ LogFlowFunc(("indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
mpVbvaMemory->indexRecordFirst, mpVbvaMemory->indexRecordFree, mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
#endif /* DEBUG_sunlover_2 */
@@ -1911,8 +2202,8 @@ void Display::videoAccelFlush (void)
if (cbCmd != 0)
{
#ifdef DEBUG_sunlover
- LogFlowFunc (("hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
- cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
+ LogFlowFunc(("hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
+ cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
#endif /* DEBUG_sunlover */
VBVACMDHDR hdrSaved = *phdr;
@@ -2024,9 +2315,10 @@ int Display::videoAccelRefreshProcess(void)
// IDisplay methods
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP Display::GetScreenResolution (ULONG aScreenId,
- ULONG *aWidth, ULONG *aHeight, ULONG *aBitsPerPixel)
+ ULONG *aWidth, ULONG *aHeight, ULONG *aBitsPerPixel,
+ LONG *aXOrigin, LONG *aYOrigin)
{
- LogRelFlowFunc (("aScreenId = %d\n", aScreenId));
+ LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
@@ -2036,10 +2328,12 @@ STDMETHODIMP Display::GetScreenResolution (ULONG aScreenId,
uint32_t u32Width = 0;
uint32_t u32Height = 0;
uint32_t u32BitsPerPixel = 0;
+ int32_t xOrigin = 0;
+ int32_t yOrigin = 0;
if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
{
- CHECK_CONSOLE_DRV (mpDrv);
+ CHECK_CONSOLE_DRV(mpDrv);
u32Width = mpDrv->IConnector.cx;
u32Height = mpDrv->IConnector.cy;
@@ -2052,6 +2346,8 @@ STDMETHODIMP Display::GetScreenResolution (ULONG aScreenId,
u32Width = pFBInfo->w;
u32Height = pFBInfo->h;
u32BitsPerPixel = pFBInfo->u16BitsPerPixel;
+ xOrigin = pFBInfo->xOrigin;
+ yOrigin = pFBInfo->yOrigin;
}
else
{
@@ -2064,33 +2360,37 @@ STDMETHODIMP Display::GetScreenResolution (ULONG aScreenId,
*aHeight = u32Height;
if (aBitsPerPixel)
*aBitsPerPixel = u32BitsPerPixel;
+ if (aXOrigin)
+ *aXOrigin = xOrigin;
+ if (aYOrigin)
+ *aYOrigin = yOrigin;
return S_OK;
}
-STDMETHODIMP Display::SetFramebuffer (ULONG aScreenId,
- IFramebuffer *aFramebuffer)
+STDMETHODIMP Display::SetFramebuffer(ULONG aScreenId, IFramebuffer *aFramebuffer)
{
- LogRelFlowFunc (("\n"));
+ LogRelFlowFunc(("\n"));
if (aFramebuffer != NULL)
CheckComArgOutPointerValid(aFramebuffer);
AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
+ if (FAILED(autoCaller.rc()))
+ return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- Console::SafeVMPtrQuiet pVM (mParent);
- if (pVM.isOk())
+ Console::SafeVMPtrQuiet ptrVM(mParent);
+ if (ptrVM.isOk())
{
/* Must release the lock here because the changeFramebuffer will
* also obtain it. */
alock.release();
/* send request to the EMT thread */
- int vrc = VMR3ReqCallWait (pVM, VMCPUID_ANY,
- (PFNRT) changeFramebuffer, 3, this, aFramebuffer, aScreenId);
+ int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY,
+ (PFNRT)changeFramebuffer, 3, this, aFramebuffer, aScreenId);
alock.acquire();
@@ -2113,7 +2413,16 @@ STDMETHODIMP Display::SetFramebuffer (ULONG aScreenId,
alock.release();
if (pVMMDev)
- vrc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_SCREEN_CHANGED, SHCRGL_CPARMS_SCREEN_CHANGED, &parm);
+ {
+#if 0
+ if (mhCrOglSvc)
+ pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_SCREEN_CHANGED, &parm, NULL, NULL);
+ else
+ AssertMsgFailed(("mhCrOglSvc is NULL\n"));
+#else
+ pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_SCREEN_CHANGED, SHCRGL_CPARMS_SCREEN_CHANGED, &parm);
+#endif
+ }
/*ComAssertRCRet (vrc, E_FAIL);*/
alock.acquire();
@@ -2131,10 +2440,10 @@ STDMETHODIMP Display::SetFramebuffer (ULONG aScreenId,
return S_OK;
}
-STDMETHODIMP Display::GetFramebuffer (ULONG aScreenId,
- IFramebuffer **aFramebuffer, LONG *aXOrigin, LONG *aYOrigin)
+STDMETHODIMP Display::GetFramebuffer(ULONG aScreenId,
+ IFramebuffer **aFramebuffer, LONG *aXOrigin, LONG *aYOrigin)
{
- LogRelFlowFunc (("aScreenId = %d\n", aScreenId));
+ LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
CheckComArgOutPointerValid(aFramebuffer);
@@ -2169,13 +2478,7 @@ STDMETHODIMP Display::SetVideoModeHint(ULONG aDisplay, BOOL aEnabled,
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- CHECK_CONSOLE_DRV (mpDrv);
-
- /* XXX Ignore these parameters for now: */
- NOREF(aChangeOrigin);
- NOREF(aOriginX);
- NOREF(aOriginY);
- NOREF(aEnabled);
+ CHECK_CONSOLE_DRV(mpDrv);
/*
* Do some rough checks for valid input
@@ -2215,7 +2518,9 @@ STDMETHODIMP Display::SetVideoModeHint(ULONG aDisplay, BOOL aEnabled,
{
PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
if (pVMMDevPort)
- pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aBitsPerPixel, aDisplay);
+ pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aBitsPerPixel,
+ aDisplay, aOriginX, aOriginY,
+ RT_BOOL(aEnabled), RT_BOOL(aChangeOrigin));
}
return S_OK;
}
@@ -2237,9 +2542,83 @@ STDMETHODIMP Display::SetSeamlessMode (BOOL enabled)
if (pVMMDevPort)
pVMMDevPort->pfnRequestSeamlessChange(pVMMDevPort, !!enabled);
}
+
+#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
+ if (!enabled)
+ {
+ BOOL is3denabled = FALSE;
+
+ mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
+
+ VMMDev *vmmDev = mParent->getVMMDev();
+ if (is3denabled && vmmDev)
+ {
+ VBOXHGCMSVCPARM parm;
+
+ parm.type = VBOX_HGCM_SVC_PARM_PTR;
+ /* NULL means disable */
+ parm.u.pointer.addr = NULL;
+ parm.u.pointer.size = 0; /* <- means null rects, NULL pRects address and 0 rects means "disable" */
+
+ if (mhCrOglSvc)
+ vmmDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_SET_VISIBLE_REGION, &parm, NULL, NULL);
+ else
+ AssertMsgFailed(("mhCrOglSvc is NULL\n"));
+
+ }
+ }
+#endif
return S_OK;
}
+#ifdef VBOX_WITH_CROGL
+BOOL Display::displayCheckTakeScreenshotCrOgl(Display *pDisplay, ULONG aScreenId, uint8_t *pu8Data, uint32_t u32Width, uint32_t u32Height)
+{
+ BOOL is3denabled;
+ pDisplay->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
+ if (is3denabled && pDisplay->mCrOglCallbacks.pfnHasData())
+ {
+ VMMDev *pVMMDev = pDisplay->mParent->getVMMDev();
+ if (pVMMDev)
+ {
+ CRVBOXHGCMTAKESCREENSHOT *pScreenshot = (CRVBOXHGCMTAKESCREENSHOT*)RTMemAlloc(sizeof (*pScreenshot));
+ if (pScreenshot)
+ {
+ /* screen id or CRSCREEN_ALL to specify all enabled */
+ pScreenshot->u32Screen = aScreenId;
+ pScreenshot->u32Width = u32Width;
+ pScreenshot->u32Height = u32Height;
+ pScreenshot->u32Pitch = u32Width * 4;
+ pScreenshot->pvBuffer = pu8Data;
+ pScreenshot->pvContext = NULL;
+ pScreenshot->pfnScreenshotBegin = NULL;
+ pScreenshot->pfnScreenshotPerform = NULL;
+ pScreenshot->pfnScreenshotEnd = NULL;
+
+ VBOXHGCMSVCPARM parm;
+
+ parm.type = VBOX_HGCM_SVC_PARM_PTR;
+ parm.u.pointer.addr = pScreenshot;
+ parm.u.pointer.size = sizeof (*pScreenshot);
+
+ int rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_TAKE_SCREENSHOT, 1, &parm);
+
+ RTMemFree(pScreenshot);
+
+ if (RT_SUCCESS(rc))
+ return TRUE;
+ else
+ {
+ AssertMsgFailed(("failed to get screenshot data from crOgl %d\n", rc));
+ /* fall back to the non-3d mechanism */
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+#endif
+
int Display::displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_t **ppu8Data, size_t *pcbData, uint32_t *pu32Width, uint32_t *pu32Height)
{
int rc;
@@ -2287,15 +2666,15 @@ int Display::displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_
uint32_t u32DstBitsPerPixel = 32;
rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
- width, height,
- pu8Src,
- xSrc, ySrc,
- u32SrcWidth, u32SrcHeight,
- u32SrcLineSize, u32SrcBitsPerPixel,
- pu8Dst,
- xDst, yDst,
- u32DstWidth, u32DstHeight,
- u32DstLineSize, u32DstBitsPerPixel);
+ width, height,
+ pu8Src,
+ xSrc, ySrc,
+ u32SrcWidth, u32SrcHeight,
+ u32SrcLineSize, u32SrcBitsPerPixel,
+ pu8Dst,
+ xDst, yDst,
+ u32DstWidth, u32DstHeight,
+ u32DstLineSize, u32DstBitsPerPixel);
if (RT_SUCCESS(rc))
{
*ppu8Data = pu8Data;
@@ -2306,6 +2685,14 @@ int Display::displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_
else
{
RTMemFree(pu8Data);
+
+ /* CopyRect can fail if VBVA was paused in VGA device, retry using the generic method. */
+ if ( rc == VERR_INVALID_STATE
+ && aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
+ {
+ rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort,
+ ppu8Data, pcbData, pu32Width, pu32Height);
+ }
}
}
}
@@ -2327,7 +2714,8 @@ int Display::displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_
return rc;
}
-static int displayTakeScreenshot(PVM pVM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv, ULONG aScreenId, BYTE *address, ULONG width, ULONG height)
+static int displayTakeScreenshot(PUVM pUVM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv, ULONG aScreenId,
+ BYTE *address, ULONG width, ULONG height)
{
uint8_t *pu8Data = NULL;
size_t cbData = 0;
@@ -2335,6 +2723,11 @@ static int displayTakeScreenshot(PVM pVM, Display *pDisplay, struct DRVMAINDISPL
uint32_t cy = 0;
int vrc = VINF_SUCCESS;
+# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
+ if (Display::displayCheckTakeScreenshotCrOgl(pDisplay, aScreenId, (uint8_t*)address, width, height))
+ return VINF_SUCCESS;
+#endif
+
int cRetries = 5;
while (cRetries-- > 0)
@@ -2342,8 +2735,8 @@ static int displayTakeScreenshot(PVM pVM, Display *pDisplay, struct DRVMAINDISPL
/* Note! Not sure if the priority call is such a good idea here, but
it would be nice to have an accurate screenshot for the bug
report if the VM deadlocks. */
- vrc = VMR3ReqPriorityCallWait(pVM, VMCPUID_ANY, (PFNRT)Display::displayTakeScreenshotEMT, 6,
- pDisplay, aScreenId, &pu8Data, &cbData, &cx, &cy);
+ vrc = VMR3ReqPriorityCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)Display::displayTakeScreenshotEMT, 6,
+ pDisplay, aScreenId, &pu8Data, &cbData, &cx, &cy);
if (vrc != VERR_TRY_AGAIN)
{
break;
@@ -2419,10 +2812,12 @@ STDMETHODIMP Display::TakeScreenShot(ULONG aScreenId, BYTE *address, ULONG width
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- CHECK_CONSOLE_DRV(mpDrv);
+ if (!mpDrv)
+ return E_FAIL;
- Console::SafeVMPtr pVM(mParent);
- if (FAILED(pVM.rc())) return pVM.rc();
+ Console::SafeVMPtr ptrVM(mParent);
+ if (!ptrVM.isOk())
+ return ptrVM.rc();
HRESULT rc = S_OK;
@@ -2435,7 +2830,7 @@ STDMETHODIMP Display::TakeScreenShot(ULONG aScreenId, BYTE *address, ULONG width
*/
alock.release();
- int vrc = displayTakeScreenshot(pVM, this, mpDrv, aScreenId, address, width, height);
+ int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, address, width, height);
if (vrc == VERR_NOT_IMPLEMENTED)
rc = setError(E_NOTIMPL,
@@ -2447,7 +2842,7 @@ STDMETHODIMP Display::TakeScreenShot(ULONG aScreenId, BYTE *address, ULONG width
rc = setError(VBOX_E_IPRT_ERROR,
tr("Could not take a screenshot (%Rrc)"), vrc);
- LogRelFlowFunc(("rc=%08X\n", rc));
+ LogRelFlowFunc(("rc=%Rhrc\n", rc));
return rc;
}
@@ -2471,10 +2866,12 @@ STDMETHODIMP Display::TakeScreenShotToArray(ULONG aScreenId, ULONG width, ULONG
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- CHECK_CONSOLE_DRV(mpDrv);
+ if (!mpDrv)
+ return E_FAIL;
- Console::SafeVMPtr pVM(mParent);
- if (FAILED(pVM.rc())) return pVM.rc();
+ Console::SafeVMPtr ptrVM(mParent);
+ if (!ptrVM.isOk())
+ return ptrVM.rc();
HRESULT rc = S_OK;
@@ -2493,7 +2890,7 @@ STDMETHODIMP Display::TakeScreenShotToArray(ULONG aScreenId, ULONG width, ULONG
if (!pu8Data)
return E_OUTOFMEMORY;
- int vrc = displayTakeScreenshot(pVM, this, mpDrv, aScreenId, pu8Data, width, height);
+ int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, pu8Data, width, height);
if (RT_SUCCESS(vrc))
{
@@ -2523,7 +2920,7 @@ STDMETHODIMP Display::TakeScreenShotToArray(ULONG aScreenId, ULONG width, ULONG
RTMemFree(pu8Data);
- LogRelFlowFunc(("rc=%08X\n", rc));
+ LogRelFlowFunc(("rc=%Rhrc\n", rc));
return rc;
}
@@ -2549,8 +2946,9 @@ STDMETHODIMP Display::TakeScreenShotPNGToArray(ULONG aScreenId, ULONG width, ULO
CHECK_CONSOLE_DRV(mpDrv);
- Console::SafeVMPtr pVM(mParent);
- if (FAILED(pVM.rc())) return pVM.rc();
+ Console::SafeVMPtr ptrVM(mParent);
+ if (!ptrVM.isOk())
+ return ptrVM.rc();
HRESULT rc = S_OK;
@@ -2569,7 +2967,7 @@ STDMETHODIMP Display::TakeScreenShotPNGToArray(ULONG aScreenId, ULONG width, ULO
if (!pu8Data)
return E_OUTOFMEMORY;
- int vrc = displayTakeScreenshot(pVM, this, mpDrv, aScreenId, pu8Data, width, height);
+ int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, pu8Data, width, height);
if (RT_SUCCESS(vrc))
{
@@ -2605,12 +3003,136 @@ STDMETHODIMP Display::TakeScreenShotPNGToArray(ULONG aScreenId, ULONG width, ULO
RTMemFree(pu8Data);
- LogRelFlowFunc(("rc=%08X\n", rc));
+ LogRelFlowFunc(("rc=%Rhrc\n", rc));
return rc;
}
+int Display::VideoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens))
+{
+#ifdef VBOX_WITH_VPX
+ com::SafeArray<BOOL> Screens(ComSafeArrayInArg(aScreens));
+ for (unsigned i = 0; i < Screens.size(); i++)
+ maVideoRecEnabled[i] = RT_BOOL(Screens[i]);
+ return VINF_SUCCESS;
+#else
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
-int Display::drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address, ULONG x, ULONG y, ULONG width, ULONG height)
+/**
+ * Start video capturing. Does nothing if capturing is already active.
+ */
+int Display::VideoCaptureStart()
+{
+#ifdef VBOX_WITH_VPX
+ if (VideoRecIsEnabled(mpVideoRecCtx))
+ return VINF_SUCCESS;
+
+ int rc = VideoRecContextCreate(&mpVideoRecCtx, mcMonitors);
+ if (RT_FAILURE(rc))
+ {
+ LogFlow(("Failed to create video recording context (%Rrc)!\n", rc));
+ return rc;
+ }
+ ComPtr<IMachine> pMachine = mParent->machine();
+ com::SafeArray<BOOL> screens;
+ HRESULT hrc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens));
+ AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
+ for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++)
+ maVideoRecEnabled[i] = i < screens.size() && screens[i];
+ ULONG ulWidth;
+ hrc = pMachine->COMGETTER(VideoCaptureWidth)(&ulWidth);
+ AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
+ ULONG ulHeight;
+ hrc = pMachine->COMGETTER(VideoCaptureHeight)(&ulHeight);
+ AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
+ ULONG ulRate;
+ hrc = pMachine->COMGETTER(VideoCaptureRate)(&ulRate);
+ AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
+ ULONG ulFPS;
+ hrc = pMachine->COMGETTER(VideoCaptureFPS)(&ulFPS);
+ AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
+ BSTR strFile;
+ hrc = pMachine->COMGETTER(VideoCaptureFile)(&strFile);
+ AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
+ RTTIMESPEC ts;
+ RTTimeNow(&ts);
+ RTTIME time;
+ RTTimeExplode(&time, &ts);
+ for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
+ {
+ char *pszAbsPath = RTPathAbsDup(com::Utf8Str(strFile).c_str());
+ char *pszExt = RTPathExt(pszAbsPath);
+ if (pszExt)
+ pszExt = RTStrDup(pszExt);
+ RTPathStripExt(pszAbsPath);
+ if (!pszAbsPath)
+ rc = VERR_INVALID_PARAMETER;
+ if (!pszExt)
+ pszExt = RTStrDup(".webm");
+ char *pszName = NULL;
+ if (RT_SUCCESS(rc))
+ {
+ if (mcMonitors > 1)
+ rc = RTStrAPrintf(&pszName, "%s-%u%s", pszAbsPath, uScreen+1, pszExt);
+ else
+ rc = RTStrAPrintf(&pszName, "%s%s", pszAbsPath, pszExt);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ rc = VideoRecStrmInit(mpVideoRecCtx, uScreen,
+ pszName, ulWidth, ulHeight, ulRate, ulFPS);
+ if (rc == VERR_ALREADY_EXISTS)
+ {
+ RTStrFree(pszName);
+ pszName = NULL;
+
+ if (mcMonitors > 1)
+ rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s",
+ pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
+ time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
+ uScreen+1, pszExt);
+ else
+ rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s",
+ pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
+ time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
+ pszExt);
+ if (RT_SUCCESS(rc))
+ rc = VideoRecStrmInit(mpVideoRecCtx, uScreen,
+ pszName, ulWidth, ulHeight, ulRate, ulFPS);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ LogRel(("WebM/VP8 video recording screen #%u with %ux%u @ %u kbps, %u fps to '%s' enabled.\n",
+ uScreen, ulWidth, ulHeight, ulRate, ulFPS, pszName));
+ else
+ LogRel(("Failed to initialize video recording context #%u (%Rrc)!\n", uScreen, rc));
+ RTStrFree(pszName);
+ RTStrFree(pszExt);
+ RTStrFree(pszAbsPath);
+ }
+ return rc;
+#else
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+
+/**
+ * Stop video capturing. Does nothing if video capturing is not active.
+ */
+void Display::VideoCaptureStop()
+{
+#ifdef VBOX_WITH_VPX
+ if (VideoRecIsEnabled(mpVideoRecCtx))
+ LogRel(("WebM/VP8 video recording stopped.\n"));
+ VideoRecContextClose(mpVideoRecCtx);
+ mpVideoRecCtx = NULL;
+#endif
+}
+
+int Display::drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address,
+ ULONG x, ULONG y, ULONG width, ULONG height)
{
int rc = VINF_SUCCESS;
pDisplay->vbvaLock();
@@ -2661,7 +3183,7 @@ int Display::drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address,
* it to update. And for default format, render the guest VRAM to framebuffer.
*/
if ( pFBInfo->fDefaultFormat
- && !(pFBInfo->fDisabled))
+ && !pFBInfo->fDisabled)
{
address = NULL;
HRESULT hrc = pFBInfo->pFramebuffer->COMGETTER(Address) (&address);
@@ -2706,15 +3228,16 @@ int Display::drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address,
rc = VERR_INVALID_PARAMETER;
}
- if (RT_SUCCESS(rc) && pDisplay->maFramebuffers[aScreenId].u32ResizeStatus == ResizeStatus_Void)
+ if ( RT_SUCCESS(rc)
+ && pDisplay->maFramebuffers[aScreenId].u32ResizeStatus == ResizeStatus_Void)
pDisplay->mParent->consoleVRDPServer()->SendUpdateBitmap(aScreenId, x, y, width, height);
pDisplay->vbvaUnlock();
return rc;
}
-STDMETHODIMP Display::DrawToScreen (ULONG aScreenId, BYTE *address, ULONG x, ULONG y,
- ULONG width, ULONG height)
+STDMETHODIMP Display::DrawToScreen(ULONG aScreenId, BYTE *address,
+ ULONG x, ULONG y, ULONG width, ULONG height)
{
/// @todo (r=dmik) this function may take too long to complete if the VM
// is doing something like saving state right now. Which, in case if it
@@ -2722,7 +3245,7 @@ STDMETHODIMP Display::DrawToScreen (ULONG aScreenId, BYTE *address, ULONG x, ULO
// check the machine state here (by enclosing the check and VMRequCall
// within the Console lock to make it atomic).
- LogRelFlowFunc (("address=%p, x=%d, y=%d, width=%d, height=%d\n",
+ LogRelFlowFunc(("address=%p, x=%d, y=%d, width=%d, height=%d\n",
(void *)address, x, y, width, height));
CheckComArgNotNull(address);
@@ -2734,10 +3257,11 @@ STDMETHODIMP Display::DrawToScreen (ULONG aScreenId, BYTE *address, ULONG x, ULO
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- CHECK_CONSOLE_DRV (mpDrv);
+ CHECK_CONSOLE_DRV(mpDrv);
- Console::SafeVMPtr pVM(mParent);
- if (FAILED(pVM.rc())) return pVM.rc();
+ Console::SafeVMPtr ptrVM(mParent);
+ if (!ptrVM.isOk())
+ return ptrVM.rc();
/* Release lock because the call scheduled on EMT may also try to take it. */
alock.release();
@@ -2746,8 +3270,8 @@ STDMETHODIMP Display::DrawToScreen (ULONG aScreenId, BYTE *address, ULONG x, ULO
* Again we're lazy and make the graphics device do all the
* dirty conversion work.
*/
- int rcVBox = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)Display::drawToScreenEMT, 7,
- this, aScreenId, address, x, y, width, height);
+ int rcVBox = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::drawToScreenEMT, 7,
+ this, aScreenId, address, x, y, width, height);
/*
* If the function returns not supported, we'll have to do all the
@@ -2769,15 +3293,15 @@ STDMETHODIMP Display::DrawToScreen (ULONG aScreenId, BYTE *address, ULONG x, ULO
// handleDisplayUpdate (x, y, width, height);
// }
- LogRelFlowFunc (("rc=%08X\n", rc));
+ LogRelFlowFunc(("rc=%Rhrc\n", rc));
return rc;
}
-void Display::InvalidateAndUpdateEMT(Display *pDisplay)
+void Display::InvalidateAndUpdateEMT(Display *pDisplay, unsigned uId, bool fUpdateAll)
{
pDisplay->vbvaLock();
unsigned uScreenId;
- for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
+ for (uScreenId = (fUpdateAll ? 0 : uId); uScreenId < pDisplay->mcMonitors; uScreenId++)
{
DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
@@ -2788,7 +3312,8 @@ void Display::InvalidateAndUpdateEMT(Display *pDisplay)
else
{
if ( !pFBInfo->pFramebuffer.isNull()
- && !(pFBInfo->fDisabled))
+ && !pFBInfo->fDisabled
+ && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
{
/* Render complete VRAM screen to the framebuffer.
* When framebuffer uses VRAM directly, just notify it to update.
@@ -2796,6 +3321,10 @@ void Display::InvalidateAndUpdateEMT(Display *pDisplay)
if (pFBInfo->fDefaultFormat)
{
BYTE *address = NULL;
+ ULONG uWidth = 0;
+ ULONG uHeight = 0;
+ pFBInfo->pFramebuffer->COMGETTER(Width) (&uWidth);
+ pFBInfo->pFramebuffer->COMGETTER(Height) (&uHeight);
HRESULT hrc = pFBInfo->pFramebuffer->COMGETTER(Address) (&address);
if (SUCCEEDED(hrc) && address != NULL)
{
@@ -2819,22 +3348,32 @@ void Display::InvalidateAndUpdateEMT(Display *pDisplay)
uint32_t u32DstLineSize = u32DstWidth * 4;
uint32_t u32DstBitsPerPixel = 32;
- pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
- width, height,
- pu8Src,
- xSrc, ySrc,
- u32SrcWidth, u32SrcHeight,
- u32SrcLineSize, u32SrcBitsPerPixel,
- pu8Dst,
- xDst, yDst,
- u32DstWidth, u32DstHeight,
- u32DstLineSize, u32DstBitsPerPixel);
+ /* if uWidth != pFBInfo->w and uHeight != pFBInfo->h
+ * implies resize of Framebuffer is in progress and
+ * copyrect should not be called.
+ */
+ if (uWidth == pFBInfo->w && uHeight == pFBInfo->h)
+ {
+
+ pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
+ width, height,
+ pu8Src,
+ xSrc, ySrc,
+ u32SrcWidth, u32SrcHeight,
+ u32SrcLineSize, u32SrcBitsPerPixel,
+ pu8Dst,
+ xDst, yDst,
+ u32DstWidth, u32DstHeight,
+ u32DstLineSize, u32DstBitsPerPixel);
+ }
}
}
pDisplay->handleDisplayUpdate (uScreenId, 0, 0, pFBInfo->w, pFBInfo->h);
}
}
+ if (!fUpdateAll)
+ break;
}
pDisplay->vbvaUnlock();
}
@@ -2854,28 +3393,29 @@ STDMETHODIMP Display::InvalidateAndUpdate()
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- CHECK_CONSOLE_DRV (mpDrv);
+ CHECK_CONSOLE_DRV(mpDrv);
- Console::SafeVMPtr pVM(mParent);
- if (FAILED(pVM.rc())) return pVM.rc();
+ Console::SafeVMPtr ptrVM(mParent);
+ if (!ptrVM.isOk())
+ return ptrVM.rc();
HRESULT rc = S_OK;
- LogRelFlowFunc (("Sending DPYUPDATE request\n"));
+ LogRelFlowFunc(("Sending DPYUPDATE request\n"));
/* Have to release the lock when calling EMT. */
alock.release();
/* pdm.h says that this has to be called from the EMT thread */
- int rcVBox = VMR3ReqCallVoidWait(pVM, VMCPUID_ANY, (PFNRT)Display::InvalidateAndUpdateEMT,
- 1, this);
+ int rcVBox = VMR3ReqCallVoidWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::InvalidateAndUpdateEMT,
+ 3, this, 0, true);
alock.acquire();
if (RT_FAILURE(rcVBox))
rc = setError(VBOX_E_IPRT_ERROR,
tr("Could not invalidate and update the screen (%Rrc)"), rcVBox);
- LogRelFlowFunc (("rc=%08X\n", rc));
+ LogRelFlowFunc(("rc=%Rhrc\n", rc));
return rc;
}
@@ -2887,7 +3427,7 @@ STDMETHODIMP Display::InvalidateAndUpdate()
*/
STDMETHODIMP Display::ResizeCompleted(ULONG aScreenId)
{
- LogRelFlowFunc (("\n"));
+ LogRelFlowFunc(("\n"));
/// @todo (dmik) can we AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); here?
// This will require general code review and may add some details.
@@ -2926,33 +3466,33 @@ STDMETHODIMP Display::CompleteVHWACommand(BYTE *pCommand)
STDMETHODIMP Display::ViewportChanged(ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
{
#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
+
+ if (mcMonitors <= aScreenId)
+ {
+ AssertMsgFailed(("invalid screen id\n"));
+ return E_INVALIDARG;
+ }
+
BOOL is3denabled;
mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
if (is3denabled)
{
- VBOXHGCMSVCPARM aParms[5];
-
- aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
- aParms[0].u.uint32 = aScreenId;
-
- aParms[1].type = VBOX_HGCM_SVC_PARM_32BIT;
- aParms[1].u.uint32 = x;
-
- aParms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
- aParms[2].u.uint32 = y;
-
-
- aParms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
- aParms[3].u.uint32 = width;
-
- aParms[4].type = VBOX_HGCM_SVC_PARM_32BIT;
- aParms[4].u.uint32 = height;
-
VMMDev *pVMMDev = mParent->getVMMDev();
if (pVMMDev)
- pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_VIEWPORT_CHANGED, SHCRGL_CPARMS_VIEWPORT_CHANGED, aParms);
+ {
+ crViewportNotify(pVMMDev, aScreenId, x, y, width, height);
+ }
+ else
+ {
+ DISPLAYFBINFO *pFb = &maFramebuffers[aScreenId];
+ pFb->pendingViewportInfo.fPending = true;
+ pFb->pendingViewportInfo.x = x;
+ pFb->pendingViewportInfo.y = y;
+ pFb->pendingViewportInfo.width = width;
+ pFb->pendingViewportInfo.height = height;
+ }
}
#endif /* VBOX_WITH_CROGL && VBOX_WITH_HGCM */
return S_OK;
@@ -2966,15 +3506,15 @@ STDMETHODIMP Display::ViewportChanged(ULONG aScreenId, ULONG x, ULONG y, ULONG w
*
* @thread EMT
*/
-void Display::updateDisplayData(void)
+int Display::updateDisplayData(void)
{
- LogRelFlowFunc (("\n"));
+ LogRelFlowFunc(("\n"));
/* the driver might not have been constructed yet */
if (!mpDrv)
- return;
+ return VINF_SUCCESS;
-#if DEBUG
+#ifdef VBOX_STRICT
/*
* Sanity check. Note that this method may be called on EMT after Console
* has started the power down procedure (but before our #drvDestruct() is
@@ -2983,9 +3523,12 @@ void Display::updateDisplayData(void)
* build to save some ms (necessary to construct SafeVMPtrQuiet) in this
* time-critical method.
*/
- Console::SafeVMPtrQuiet pVM (mParent);
- if (pVM.isOk())
- VM_ASSERT_EMT (pVM.raw());
+ Console::SafeVMPtrQuiet ptrVM(mParent);
+ if (ptrVM.isOk())
+ {
+ PVM pVM = VMR3GetVM(ptrVM.rawUVM());
+ Assert(VM_IS_EMT(pVM));
+ }
#endif
/* The method is only relevant to the primary framebuffer. */
@@ -3010,6 +3553,14 @@ void Display::updateDisplayData(void)
rc = pFramebuffer->COMGETTER(Height) (&height);
AssertComRC (rc);
+ if ( (width != mLastWidth && mLastWidth != 0)
+ || (height != mLastHeight && mLastHeight != 0))
+ {
+ LogRel(("updateDisplayData: size mismatch w %d(%d) h %d(%d)\n",
+ width, mLastWidth, height, mLastHeight));
+ return VERR_INVALID_STATE;
+ }
+
mpDrv->IConnector.pu8Data = (uint8_t *) address;
mpDrv->IConnector.cbScanline = bytesPerLine;
mpDrv->IConnector.cBits = bitsPerPixel;
@@ -3025,8 +3576,57 @@ void Display::updateDisplayData(void)
mpDrv->IConnector.cx = 0;
mpDrv->IConnector.cy = 0;
}
- LogRelFlowFunc (("leave\n"));
+ LogRelFlowFunc(("leave\n"));
+ return VINF_SUCCESS;
+}
+
+#ifdef VBOX_WITH_CROGL
+void Display::crViewportNotify(VMMDev *pVMMDev, ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
+{
+#if 0
+ VBOXHGCMSVCPARM parm;
+
+ CRVBOXHGCMVIEWPORT *pViewportInfo = (CRVBOXHGCMVIEWPORT*)RTMemAlloc(sizeof (*pViewportInfo));
+ if(!pViewportInfo)
+ {
+ AssertMsgFailed(("RTMemAlloc failed!\n"));
+ return;
+ }
+
+ pViewportInfo->u32Screen = aScreenId;
+ pViewportInfo->x = x;
+ pViewportInfo->y = y;
+ pViewportInfo->width = width;
+ pViewportInfo->height = height;
+
+ parm.type = VBOX_HGCM_SVC_PARM_PTR;
+ parm.u.pointer.addr = pViewportInfo;
+ parm.u.pointer.size = sizeof (*pViewportInfo);
+
+ pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_VIEWPORT_CHANGED2, &parm, displayCrAsyncCmdCompletion, this);
+#else
+ VBOXHGCMSVCPARM aParms[5];
+
+ aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
+ aParms[0].u.uint32 = aScreenId;
+
+ aParms[1].type = VBOX_HGCM_SVC_PARM_32BIT;
+ aParms[1].u.uint32 = x;
+
+ aParms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
+ aParms[2].u.uint32 = y;
+
+
+ aParms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
+ aParms[3].u.uint32 = width;
+
+ aParms[4].type = VBOX_HGCM_SVC_PARM_32BIT;
+ aParms[4].u.uint32 = height;
+
+ pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_VIEWPORT_CHANGED, SHCRGL_CPARMS_VIEWPORT_CHANGED, aParms);
+#endif
}
+#endif
#ifdef VBOX_WITH_CRHGSMI
void Display::setupCrHgsmiData(void)
@@ -3041,8 +3641,8 @@ void Display::setupCrHgsmiData(void)
{
Assert(mhCrOglSvc);
/* setup command completion callback */
- VBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_COMPLETION Completion;
- Completion.Hdr.enmType = VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION;
+ VBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB Completion;
+ Completion.Hdr.enmType = VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB;
Completion.Hdr.cbCmd = sizeof (Completion);
Completion.hCompletion = mpDrv->pVBVACallbacks;
Completion.pfnCompletion = mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync;
@@ -3054,7 +3654,23 @@ void Display::setupCrHgsmiData(void)
rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_CRHGSMI_CTL, 1, &parm);
if (RT_SUCCESS(rc))
+ {
+ ULONG ul;
+
+ for (ul = 0; ul < mcMonitors; ul++)
+ {
+ DISPLAYFBINFO *pFb = &maFramebuffers[ul];
+ if (!pFb->pendingViewportInfo.fPending)
+ continue;
+
+ crViewportNotify(pVMMDev, ul, pFb->pendingViewportInfo.x, pFb->pendingViewportInfo.y, pFb->pendingViewportInfo.width, pFb->pendingViewportInfo.height);
+ pFb->pendingViewportInfo.fPending = false;
+ }
+
+ mCrOglCallbacks = Completion.MainInterface;
+
return;
+ }
AssertMsgFailed(("VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION failed rc %d", rc));
}
@@ -3079,7 +3695,7 @@ void Display::destructCrHgsmiData(void)
DECLCALLBACK(int) Display::changeFramebuffer (Display *that, IFramebuffer *aFB,
unsigned uScreenId)
{
- LogRelFlowFunc (("uScreenId = %d\n", uScreenId));
+ LogRelFlowFunc(("uScreenId = %d\n", uScreenId));
AssertReturn(that, VERR_INVALID_PARAMETER);
AssertReturn(uScreenId < that->mcMonitors, VERR_INVALID_PARAMETER);
@@ -3137,7 +3753,7 @@ DECLCALLBACK(int) Display::changeFramebuffer (Display *that, IFramebuffer *aFB,
}
}
- LogRelFlowFunc (("leave\n"));
+ LogRelFlowFunc(("leave\n"));
return VINF_SUCCESS;
}
@@ -3151,7 +3767,7 @@ DECLCALLBACK(int) Display::displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterfac
{
PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
- LogRelFlowFunc (("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
+ LogRelFlowFunc(("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
bpp, pvVRAM, cbLine, cx, cy));
return pDrv->pDisplay->handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy, VBVA_SCREEN_F_ACTIVE);
@@ -3168,8 +3784,8 @@ DECLCALLBACK(void) Display::displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterfa
PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
#ifdef DEBUG_sunlover
- LogFlowFunc (("mfVideoAccelEnabled = %d, %d,%d %dx%d\n",
- pDrv->pDisplay->mfVideoAccelEnabled, x, y, cx, cy));
+ LogFlowFunc(("mfVideoAccelEnabled = %d, %d,%d %dx%d\n",
+ pDrv->pDisplay->mfVideoAccelEnabled, x, y, cx, cy));
#endif /* DEBUG_sunlover */
/* This call does update regardless of VBVA status.
@@ -3184,25 +3800,26 @@ DECLCALLBACK(void) Display::displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterfa
* Periodic display refresh callback.
*
* @see PDMIDISPLAYCONNECTOR::pfnRefresh
+ * @thread EMT
*/
DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
{
PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
#ifdef DEBUG_sunlover
- STAM_PROFILE_START(&StatDisplayRefresh, a);
+ STAM_PROFILE_START(&g_StatDisplayRefresh, a);
#endif /* DEBUG_sunlover */
#ifdef DEBUG_sunlover_2
- LogFlowFunc (("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
- pDrv->pDisplay->mfVideoAccelEnabled));
+ LogFlowFunc(("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
+ pDrv->pDisplay->mfVideoAccelEnabled));
#endif /* DEBUG_sunlover_2 */
Display *pDisplay = pDrv->pDisplay;
bool fNoUpdate = false; /* Do not update the display if any of the framebuffers is being resized. */
unsigned uScreenId;
- LogFlow(("DisplayRefreshCallback\n"));
+ Log2(("DisplayRefreshCallback\n"));
for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
{
DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
@@ -3214,7 +3831,7 @@ DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterf
if (u32ResizeStatus == ResizeStatus_UpdateDisplayData)
{
- LogRelFlowFunc (("ResizeStatus_UpdateDisplayData %d\n", uScreenId));
+ LogRelFlowFunc(("ResizeStatus_UpdateDisplayData %d\n", uScreenId));
fNoUpdate = true; /* Always set it here, because pfnUpdateDisplayAll can cause a new resize. */
/* The framebuffer was resized and display data need to be updated. */
pDisplay->handleResizeCompletedEMT ();
@@ -3226,12 +3843,12 @@ DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterf
/* Continue with normal processing because the status here is ResizeStatus_Void.
* Repaint all displays because VM continued to run during the framebuffer resize.
*/
- pDisplay->InvalidateAndUpdateEMT(pDisplay);
+ pDisplay->InvalidateAndUpdateEMT(pDisplay, uScreenId, false);
}
else if (u32ResizeStatus == ResizeStatus_InProgress)
{
/* The framebuffer is being resized. Do not call the VGA device back. Immediately return. */
- LogRelFlowFunc (("ResizeStatus_InProcess\n"));
+ LogRelFlowFunc(("ResizeStatus_InProcess\n"));
fNoUpdate = true;
continue;
}
@@ -3275,72 +3892,94 @@ DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterf
}
#ifdef VBOX_WITH_VPX
- if (VideoRecIsEnabled(pDisplay->mpVideoRecContext))
- {
- uint32_t u32VideoRecImgFormat = VPX_IMG_FMT_NONE;
- ULONG ulGuestHeight = 0;
- ULONG ulGuestWidth = 0;
- ULONG ulBitsPerPixel;
- int rc;
- DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN];
-
- if ( !pFBInfo->pFramebuffer.isNull()
- && !(pFBInfo->fDisabled)
- && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
- {
- if (pFBInfo->fVBVAEnabled && pFBInfo->pu8FramebufferVRAM)
- {
- rc = VideoRecCopyToIntBuffer(pDisplay->mpVideoRecContext, 0, 0,
- FramebufferPixelFormat_FOURCC_RGB, pFBInfo->u16BitsPerPixel,
- pFBInfo->u32LineSize, pFBInfo->w, pFBInfo->h,
- pFBInfo->pu8FramebufferVRAM);
- ulGuestWidth = pFBInfo->w;
- ulGuestHeight = pFBInfo->h;
- ulBitsPerPixel = pFBInfo->u16BitsPerPixel;
- }
- else
+ if (VideoRecIsEnabled(pDisplay->mpVideoRecCtx))
+ {
+ do {
+# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
+ BOOL is3denabled;
+ pDisplay->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
+ if (is3denabled)
{
- rc = VideoRecCopyToIntBuffer(pDisplay->mpVideoRecContext, 0, 0,
- FramebufferPixelFormat_FOURCC_RGB, pDrv->IConnector.cBits,
- pDrv->IConnector.cbScanline, pDrv->IConnector.cx,
- pDrv->IConnector.cy, pDrv->IConnector.pu8Data);
- ulGuestWidth = pDrv->IConnector.cx;
- ulGuestHeight = pDrv->IConnector.cy;
- ulBitsPerPixel = pDrv->IConnector.cBits;
- }
+ if (ASMAtomicCmpXchgU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_SUBMITTED, CRVREC_STATE_IDLE))
+ {
+ if (pDisplay->mCrOglCallbacks.pfnHasData())
+ {
+ /* submit */
+
+ VBOXHGCMSVCPARM parm;
+
+ parm.type = VBOX_HGCM_SVC_PARM_PTR;
+ parm.u.pointer.addr = &pDisplay->mCrOglScreenshotData;
+ parm.u.pointer.size = sizeof (pDisplay->mCrOglScreenshotData);
+
+ VMMDev *pVMMDev = pDisplay->mParent->getVMMDev();
+ if (pVMMDev)
+ {
+ int rc = pVMMDev->hgcmHostFastCallAsync(pDisplay->mhCrOglSvc, SHCRGL_HOST_FN_TAKE_SCREENSHOT, &parm, displayVRecCompletion, pDisplay);
+ if (RT_SUCCESS(rc))
+ break;
+ else
+ AssertMsgFailed(("hgcmHostFastCallAsync failed %f\n", rc));
+ }
+ else
+ AssertMsgFailed(("no VMMDev\n"));
+ }
- switch (ulBitsPerPixel)
- {
- case 32:
- u32VideoRecImgFormat = VPX_IMG_FMT_RGB32;
- Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB32\n"));
- break;
- case 24:
- u32VideoRecImgFormat = VPX_IMG_FMT_RGB24;
- Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB24\n"));
- break;
- case 16:
- u32VideoRecImgFormat = VPX_IMG_FMT_RGB565;
- Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB565\n"));
- break;
- default:
- Log2(("No Proper Format detected\n"));
+ /* no 3D data available, or error has occured,
+ * go the straight way */
+ ASMAtomicWriteU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_IDLE);
+ }
+ else
+ {
+ /* record request is still in progress, don't do anything */
break;
+ }
}
+# endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
- /* Just return in case of failure without any assertion */
- if( RT_SUCCESS(rc))
- if (RT_SUCCESS(VideoRecDoRGBToYUV(pDisplay->mpVideoRecContext, u32VideoRecImgFormat)))
- VideoRecEncodeAndWrite(pDisplay->mpVideoRecContext, ulGuestWidth, ulGuestHeight);
- }
+ uint64_t u64Now = RTTimeProgramMilliTS();
+ for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
+ {
+ if (!pDisplay->maVideoRecEnabled[uScreenId])
+ continue;
+
+ DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
+
+ if ( !pFBInfo->pFramebuffer.isNull()
+ && !pFBInfo->fDisabled
+ && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
+ {
+ int rc;
+ if ( pFBInfo->fVBVAEnabled
+ && pFBInfo->pu8FramebufferVRAM)
+ {
+ rc = VideoRecCopyToIntBuf(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
+ FramebufferPixelFormat_FOURCC_RGB,
+ pFBInfo->u16BitsPerPixel,
+ pFBInfo->u32LineSize, pFBInfo->w, pFBInfo->h,
+ pFBInfo->pu8FramebufferVRAM, u64Now);
+ }
+ else
+ {
+ rc = VideoRecCopyToIntBuf(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
+ FramebufferPixelFormat_FOURCC_RGB,
+ pDrv->IConnector.cBits,
+ pDrv->IConnector.cbScanline, pDrv->IConnector.cx,
+ pDrv->IConnector.cy, pDrv->IConnector.pu8Data, u64Now);
+ }
+ if (rc == VINF_TRY_AGAIN)
+ break;
+ }
+ }
+ } while (0);
}
-#endif
+#endif /* VBOX_WITH_VPX */
#ifdef DEBUG_sunlover
- STAM_PROFILE_STOP(&StatDisplayRefresh, a);
+ STAM_PROFILE_STOP(&g_StatDisplayRefresh, a);
#endif /* DEBUG_sunlover */
#ifdef DEBUG_sunlover_2
- LogFlowFunc (("leave\n"));
+ LogFlowFunc(("leave\n"));
#endif /* DEBUG_sunlover_2 */
}
@@ -3353,7 +3992,7 @@ DECLCALLBACK(void) Display::displayResetCallback(PPDMIDISPLAYCONNECTOR pInterfac
{
PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
- LogRelFlowFunc (("\n"));
+ LogRelFlowFunc(("\n"));
/* Disable VBVA mode. */
pDrv->pDisplay->VideoAccelEnable (false, NULL);
@@ -3368,7 +4007,7 @@ DECLCALLBACK(void) Display::displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR p
{
PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
- LogRelFlowFunc (("fEnabled=%d\n", fEnabled));
+ LogRelFlowFunc(("fEnabled=%d\n", fEnabled));
NOREF(fEnabled);
@@ -3611,48 +4250,42 @@ DECLCALLBACK(void) Display::displayProcessDisplayDataCallback(PPDMIDISPLAYCONNEC
#ifdef VBOX_WITH_VIDEOHWACCEL
-void Display::handleVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
+#ifndef S_FALSE
+# define S_FALSE ((HRESULT)1L)
+#endif
+
+int Display::handleVHWACommandProcess(PVBOXVHWACMD pCommand)
{
unsigned id = (unsigned)pCommand->iDisplay;
int rc = VINF_SUCCESS;
- if (id < mcMonitors)
- {
- IFramebuffer *pFramebuffer = maFramebuffers[id].pFramebuffer;
-#ifdef DEBUG_misha
- Assert (pFramebuffer);
-#endif
+ if (id >= mcMonitors)
+ return VERR_INVALID_PARAMETER;
- if (pFramebuffer != NULL)
- {
- HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE*)pCommand);
- if (FAILED(hr))
- {
- rc = (hr == E_NOTIMPL) ? VERR_NOT_IMPLEMENTED : VERR_GENERAL_FAILURE;
- }
- }
- else
- {
- rc = VERR_NOT_IMPLEMENTED;
- }
- }
- else
- {
- rc = VERR_INVALID_PARAMETER;
- }
+ ComPtr<IFramebuffer> pFramebuffer;
+ AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
+ pFramebuffer = maFramebuffers[id].pFramebuffer;
+ arlock.release();
- if (RT_FAILURE(rc))
- {
- /* tell the guest the command is complete */
- pCommand->Flags &= (~VBOXVHWACMD_FLAG_HG_ASYNCH);
- pCommand->rc = rc;
- }
+ if (pFramebuffer == NULL)
+ return VERR_INVALID_STATE; /* notify we can not handle request atm */
+
+ HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE*)pCommand);
+ if (hr == S_FALSE)
+ return VINF_SUCCESS;
+ else if (SUCCEEDED(hr))
+ return VINF_CALLBACK_RETURN;
+ else if (hr == E_ACCESSDENIED)
+ return VERR_INVALID_STATE; /* notify we can not handle request atm */
+ else if (hr == E_NOTIMPL)
+ return VERR_NOT_IMPLEMENTED;
+ return VERR_GENERAL_FAILURE;
}
-DECLCALLBACK(void) Display::displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
+DECLCALLBACK(int) Display::displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
{
PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
- pDrv->pDisplay->handleVHWACommandProcess(pInterface, pCommand);
+ return pDrv->pDisplay->handleVHWACommandProcess(pCommand);
}
#endif
@@ -3667,9 +4300,9 @@ void Display::handleCrHgsmiControlCompletion(int32_t result, uint32_t u32Functio
mpDrv->pVBVACallbacks->pfnCrHgsmiControlCompleteAsync(mpDrv->pVBVACallbacks, (PVBOXVDMACMD_CHROMIUM_CTL)pParam->u.pointer.addr, result);
}
-void Display::handleCrHgsmiCommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
+void Display::handleCrHgsmiCommandProcess(PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
{
- int rc = VERR_INVALID_FUNCTION;
+ int rc = VERR_NOT_SUPPORTED;
VBOXHGCMSVCPARM parm;
parm.type = VBOX_HGCM_SVC_PARM_PTR;
parm.u.pointer.addr = pCmd;
@@ -3695,9 +4328,9 @@ void Display::handleCrHgsmiCommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBO
handleCrHgsmiCommandCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm);
}
-void Display::handleCrHgsmiControlProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCtl, uint32_t cbCtl)
+void Display::handleCrHgsmiControlProcess(PVBOXVDMACMD_CHROMIUM_CTL pCtl, uint32_t cbCtl)
{
- int rc = VERR_INVALID_FUNCTION;
+ int rc = VERR_NOT_SUPPORTED;
VBOXHGCMSVCPARM parm;
parm.type = VBOX_HGCM_SVC_PARM_PTR;
parm.u.pointer.addr = pCtl;
@@ -3721,19 +4354,18 @@ void Display::handleCrHgsmiControlProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBO
handleCrHgsmiControlCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm);
}
-
DECLCALLBACK(void) Display::displayCrHgsmiCommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
{
PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
- pDrv->pDisplay->handleCrHgsmiCommandProcess(pInterface, pCmd, cbCmd);
+ pDrv->pDisplay->handleCrHgsmiCommandProcess(pCmd, cbCmd);
}
DECLCALLBACK(void) Display::displayCrHgsmiControlProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCmd, uint32_t cbCmd)
{
PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
- pDrv->pDisplay->handleCrHgsmiControlProcess(pInterface, pCmd, cbCmd);
+ pDrv->pDisplay->handleCrHgsmiControlProcess(pCmd, cbCmd);
}
DECLCALLBACK(void) Display::displayCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
@@ -3750,6 +4382,131 @@ DECLCALLBACK(void) Display::displayCrHgsmiControlCompletion(int32_t result, uint
}
#endif
+DECLCALLBACK(void) Display::displayCrHgcmCtlSubmitCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
+{
+ VBOXCRCMDCTL *pCmd = (VBOXCRCMDCTL*)pParam->u.pointer.addr;
+ if (pCmd->pfnInternal)
+ ((PFNCRCTLCOMPLETION)pCmd->pfnInternal)(pCmd, pParam->u.pointer.size, result, pvContext);
+}
+
+int Display::handleCrHgcmCtlSubmit(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
+ PFNCRCTLCOMPLETION pfnCompletion,
+ void *pvCompletion)
+{
+ VMMDev *pVMMDev = mParent->getVMMDev();
+ if (!pVMMDev)
+ {
+ AssertMsgFailed(("no vmmdev\n"));
+ return VERR_INVALID_STATE;
+ }
+
+ Assert(mhCrOglSvc);
+ VBOXHGCMSVCPARM parm;
+ parm.type = VBOX_HGCM_SVC_PARM_PTR;
+ parm.u.pointer.addr = pCmd;
+ parm.u.pointer.size = cbCmd;
+
+ pCmd->pfnInternal = (void(*)())pfnCompletion;
+ int rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CTL, &parm, displayCrHgcmCtlSubmitCompletion, pvCompletion);
+ if (!RT_SUCCESS(rc))
+ AssertMsgFailed(("hgcmHostFastCallAsync failed rc %n", rc));
+
+ return rc;
+}
+
+DECLCALLBACK(int) Display::displayCrHgcmCtlSubmit(PPDMIDISPLAYCONNECTOR pInterface,
+ struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
+ PFNCRCTLCOMPLETION pfnCompletion,
+ void *pvCompletion)
+{
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ Display *pThis = pDrv->pDisplay;
+ return pThis->handleCrHgcmCtlSubmit(pCmd, cbCmd, pfnCompletion, pvCompletion);
+}
+
+#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
+DECLCALLBACK(void) Display::displayCrAsyncCmdCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
+{
+ Display *pDisplay = (Display *)pvContext;
+ pDisplay->handleCrAsyncCmdCompletion(result, u32Function, pParam);
+}
+
+
+void Display::handleCrAsyncCmdCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
+{
+ if (pParam->type == VBOX_HGCM_SVC_PARM_PTR && pParam->u.pointer.addr)
+ RTMemFree(pParam->u.pointer.addr);
+}
+
+bool Display::handleCrVRecScreenshotBegin(uint32_t uScreen, uint64_t u64TimeStamp)
+{
+# if VBOX_WITH_VPX
+ return VideoRecIsReady(mpVideoRecCtx, uScreen, u64TimeStamp);
+# else
+ return false;
+# endif
+}
+
+void Display::handleCrVRecScreenshotEnd(uint32_t uScreen, uint64_t u64TimeStamp)
+{
+}
+
+void Display::handleCrVRecScreenshotPerform(uint32_t uScreen,
+ uint32_t x, uint32_t y, uint32_t uPixelFormat,
+ uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
+ uint32_t uGuestWidth, uint32_t uGuestHeight,
+ uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
+{
+ Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
+# if VBOX_WITH_VPX
+ int rc = VideoRecCopyToIntBuf(mpVideoRecCtx, uScreen, x, y,
+ uPixelFormat,
+ uBitsPerPixel, uBytesPerLine,
+ uGuestWidth, uGuestHeight,
+ pu8BufferAddress, u64TimeStamp);
+ Assert(rc == VINF_SUCCESS /* || rc == VERR_TRY_AGAIN || rc == VINF_TRY_AGAIN*/);
+# endif
+}
+
+void Display::handleVRecCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
+{
+ Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
+ ASMAtomicWriteU32(&mfCrOglVideoRecState, CRVREC_STATE_IDLE);
+}
+
+DECLCALLBACK(void) Display::displayCrVRecScreenshotPerform(void *pvCtx, uint32_t uScreen,
+ uint32_t x, uint32_t y,
+ uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
+ uint32_t uGuestWidth, uint32_t uGuestHeight,
+ uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
+{
+ Display *pDisplay = (Display *)pvCtx;
+ pDisplay->handleCrVRecScreenshotPerform(uScreen,
+ x, y, FramebufferPixelFormat_FOURCC_RGB, uBitsPerPixel,
+ uBytesPerLine, uGuestWidth, uGuestHeight,
+ pu8BufferAddress, u64TimeStamp);
+}
+
+DECLCALLBACK(bool) Display::displayCrVRecScreenshotBegin(void *pvCtx, uint32_t uScreen, uint64_t u64TimeStamp)
+{
+ Display *pDisplay = (Display *)pvCtx;
+ return pDisplay->handleCrVRecScreenshotBegin(uScreen, u64TimeStamp);
+}
+
+DECLCALLBACK(void) Display::displayCrVRecScreenshotEnd(void *pvCtx, uint32_t uScreen, uint64_t u64TimeStamp)
+{
+ Display *pDisplay = (Display *)pvCtx;
+ pDisplay->handleCrVRecScreenshotEnd(uScreen, u64TimeStamp);
+}
+
+DECLCALLBACK(void) Display::displayVRecCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
+{
+ Display *pDisplay = (Display *)pvContext;
+ pDisplay->handleVRecCompletion(result, u32Function, pParam, pvContext);
+}
+
+#endif
+
#ifdef VBOX_WITH_HGSMI
DECLCALLBACK(int) Display::displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, PVBVAHOSTFLAGS pHostFlags)
@@ -3868,7 +4625,7 @@ DECLCALLBACK(void) Display::displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInte
pDrv->pUpPort->pfnUpdateDisplayRect (pDrv->pUpPort, pCmd->x, pCmd->y, pCmd->w, pCmd->h);
}
else if ( !pFBInfo->pFramebuffer.isNull()
- && !(pFBInfo->fDisabled))
+ && !pFBInfo->fDisabled)
{
/* Render VRAM content to the framebuffer. */
BYTE *address = NULL;
@@ -3916,7 +4673,7 @@ DECLCALLBACK(void) Display::displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInte
pHdrUnconst->y -= (int16_t)pFBInfo->yOrigin;
/* @todo new SendUpdate entry which can get a separate cmd header or coords. */
- pThis->mParent->consoleVRDPServer()->SendUpdate (uScreenId, pCmd, cbCmd);
+ pThis->mParent->consoleVRDPServer()->SendUpdate (uScreenId, pCmd, (uint32_t)cbCmd);
*pHdrUnconst = hdrSaved;
}
@@ -4078,14 +4835,18 @@ DECLCALLBACK(int) Display::displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, c
if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
{
+ pThis->notifyCroglResize(pView, pScreen, pvVRAM);
+
pFBInfo->fDisabled = true;
pFBInfo->flags = pScreen->u16Flags;
- /* Temporary: ask framebuffer to resize using a default format. The framebuffer will be black. */
- pThis->handleDisplayResize(pScreen->u32ViewIndex, 0,
- (uint8_t *)NULL,
- pScreen->u32LineSize, pScreen->u32Width,
- pScreen->u32Height, pScreen->u16Flags);
+ /* Ask the framebuffer to resize using a default format. The framebuffer will be black.
+ * So if the frontend does not support GuestMonitorChangedEventType_Disabled event,
+ * the VM window will be black. */
+ uint32_t u32Width = pFBInfo->w ? pFBInfo->w : 640;
+ uint32_t u32Height = pFBInfo->h ? pFBInfo->h : 480;
+ pThis->handleDisplayResize(pScreen->u32ViewIndex, 0, (uint8_t *)NULL, 0,
+ u32Width, u32Height, pScreen->u16Flags);
fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
GuestMonitorChangedEventType_Disabled,
@@ -4099,17 +4860,6 @@ DECLCALLBACK(int) Display::displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, c
*/
bool fResize = pFBInfo->fDisabled || pFBInfo->pFramebuffer.isNull();
- if (pFBInfo->fDisabled)
- {
- pFBInfo->fDisabled = false;
- fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
- GuestMonitorChangedEventType_Enabled,
- pScreen->u32ViewIndex,
- pScreen->i32OriginX, pScreen->i32OriginY,
- pScreen->u32Width, pScreen->u32Height);
- /* Continue to update pFBInfo. */
- }
-
/* Check if this is a real resize or a notification about the screen origin.
* The guest uses this VBVAResize call for both.
*/
@@ -4123,6 +4873,20 @@ DECLCALLBACK(int) Display::displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, c
bool fNewOrigin = pFBInfo->xOrigin != pScreen->i32OriginX
|| pFBInfo->yOrigin != pScreen->i32OriginY;
+ if (fNewOrigin || fResize)
+ pThis->notifyCroglResize(pView, pScreen, pvVRAM);
+
+ if (pFBInfo->fDisabled)
+ {
+ pFBInfo->fDisabled = false;
+ fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
+ GuestMonitorChangedEventType_Enabled,
+ pScreen->u32ViewIndex,
+ pScreen->i32OriginX, pScreen->i32OriginY,
+ pScreen->u32Width, pScreen->u32Height);
+ /* Continue to update pFBInfo. */
+ }
+
pFBInfo->u32Offset = pView->u32ViewOffset; /* Not used in HGSMI. */
pFBInfo->u32MaxFramebufferSize = pView->u32MaxScreenSize; /* Not used in HGSMI. */
pFBInfo->u32InformationSize = 0; /* Not used in HGSMI. */
@@ -4148,34 +4912,13 @@ DECLCALLBACK(int) Display::displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, c
0, 0);
}
-#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
- if (fNewOrigin && !fResize)
- {
- BOOL is3denabled;
- pThis->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
-
- if (is3denabled)
- {
- VBOXHGCMSVCPARM parm;
-
- parm.type = VBOX_HGCM_SVC_PARM_32BIT;
- parm.u.uint32 = pScreen->u32ViewIndex;
-
- VMMDev *pVMMDev = pThis->mParent->getVMMDev();
-
- if (pVMMDev)
- pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_SCREEN_CHANGED, SHCRGL_CPARMS_SCREEN_CHANGED, &parm);
- }
- }
-#endif /* VBOX_WITH_CROGL */
-
if (!fResize)
{
/* No parameters of the framebuffer have actually changed. */
if (fNewOrigin)
{
/* VRDP server still need this notification. */
- LogRelFlowFunc (("Calling VRDP\n"));
+ LogRelFlowFunc(("Calling VRDP\n"));
pThis->mParent->consoleVRDPServer()->SendResize();
}
return VINF_SUCCESS;
@@ -4244,23 +4987,26 @@ DECLCALLBACK(void *) Display::drvQueryInterface(PPDMIBASE pInterface, const cha
*/
DECLCALLBACK(void) Display::drvDestruct(PPDMDRVINS pDrvIns)
{
- PDRVMAINDISPLAY pData = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
- LogRelFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
+ LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
- if (pData->pDisplay)
+ if (pThis->pDisplay)
{
- AutoWriteLock displayLock(pData->pDisplay COMMA_LOCKVAL_SRC_POS);
+ AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
+#ifdef VBOX_WITH_VPX
+ pThis->pDisplay->VideoCaptureStop();
+#endif
#ifdef VBOX_WITH_CRHGSMI
- pData->pDisplay->destructCrHgsmiData();
+ pThis->pDisplay->destructCrHgsmiData();
#endif
- pData->pDisplay->mpDrv = NULL;
- pData->pDisplay->mpVMMDev = NULL;
- pData->pDisplay->mLastAddress = NULL;
- pData->pDisplay->mLastBytesPerLine = 0;
- pData->pDisplay->mLastBitsPerPixel = 0,
- pData->pDisplay->mLastWidth = 0;
- pData->pDisplay->mLastHeight = 0;
+ pThis->pDisplay->mpDrv = NULL;
+ pThis->pDisplay->mpVMMDev = NULL;
+ pThis->pDisplay->mLastAddress = NULL;
+ pThis->pDisplay->mLastBytesPerLine = 0;
+ pThis->pDisplay->mLastBitsPerPixel = 0,
+ pThis->pDisplay->mLastWidth = 0;
+ pThis->pDisplay->mLastHeight = 0;
}
}
@@ -4272,9 +5018,9 @@ DECLCALLBACK(void) Display::drvDestruct(PPDMDRVINS pDrvIns)
*/
DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
- PDRVMAINDISPLAY pData = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
- LogRelFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
+ LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
/*
* Validate configuration.
@@ -4288,44 +5034,45 @@ DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint
/*
* Init Interfaces.
*/
- pDrvIns->IBase.pfnQueryInterface = Display::drvQueryInterface;
-
- pData->IConnector.pfnResize = Display::displayResizeCallback;
- pData->IConnector.pfnUpdateRect = Display::displayUpdateCallback;
- pData->IConnector.pfnRefresh = Display::displayRefreshCallback;
- pData->IConnector.pfnReset = Display::displayResetCallback;
- pData->IConnector.pfnLFBModeChange = Display::displayLFBModeChangeCallback;
- pData->IConnector.pfnProcessAdapterData = Display::displayProcessAdapterDataCallback;
- pData->IConnector.pfnProcessDisplayData = Display::displayProcessDisplayDataCallback;
+ pDrvIns->IBase.pfnQueryInterface = Display::drvQueryInterface;
+
+ pThis->IConnector.pfnResize = Display::displayResizeCallback;
+ pThis->IConnector.pfnUpdateRect = Display::displayUpdateCallback;
+ pThis->IConnector.pfnRefresh = Display::displayRefreshCallback;
+ pThis->IConnector.pfnReset = Display::displayResetCallback;
+ pThis->IConnector.pfnLFBModeChange = Display::displayLFBModeChangeCallback;
+ pThis->IConnector.pfnProcessAdapterData = Display::displayProcessAdapterDataCallback;
+ pThis->IConnector.pfnProcessDisplayData = Display::displayProcessDisplayDataCallback;
#ifdef VBOX_WITH_VIDEOHWACCEL
- pData->IConnector.pfnVHWACommandProcess = Display::displayVHWACommandProcess;
+ pThis->IConnector.pfnVHWACommandProcess = Display::displayVHWACommandProcess;
#endif
#ifdef VBOX_WITH_CRHGSMI
- pData->IConnector.pfnCrHgsmiCommandProcess = Display::displayCrHgsmiCommandProcess;
- pData->IConnector.pfnCrHgsmiControlProcess = Display::displayCrHgsmiControlProcess;
+ pThis->IConnector.pfnCrHgsmiCommandProcess = Display::displayCrHgsmiCommandProcess;
+ pThis->IConnector.pfnCrHgsmiControlProcess = Display::displayCrHgsmiControlProcess;
#endif
+ pThis->IConnector.pfnCrHgcmCtlSubmit = Display::displayCrHgcmCtlSubmit;
#ifdef VBOX_WITH_HGSMI
- pData->IConnector.pfnVBVAEnable = Display::displayVBVAEnable;
- pData->IConnector.pfnVBVADisable = Display::displayVBVADisable;
- pData->IConnector.pfnVBVAUpdateBegin = Display::displayVBVAUpdateBegin;
- pData->IConnector.pfnVBVAUpdateProcess = Display::displayVBVAUpdateProcess;
- pData->IConnector.pfnVBVAUpdateEnd = Display::displayVBVAUpdateEnd;
- pData->IConnector.pfnVBVAResize = Display::displayVBVAResize;
- pData->IConnector.pfnVBVAMousePointerShape = Display::displayVBVAMousePointerShape;
+ pThis->IConnector.pfnVBVAEnable = Display::displayVBVAEnable;
+ pThis->IConnector.pfnVBVADisable = Display::displayVBVADisable;
+ pThis->IConnector.pfnVBVAUpdateBegin = Display::displayVBVAUpdateBegin;
+ pThis->IConnector.pfnVBVAUpdateProcess = Display::displayVBVAUpdateProcess;
+ pThis->IConnector.pfnVBVAUpdateEnd = Display::displayVBVAUpdateEnd;
+ pThis->IConnector.pfnVBVAResize = Display::displayVBVAResize;
+ pThis->IConnector.pfnVBVAMousePointerShape = Display::displayVBVAMousePointerShape;
#endif
/*
* Get the IDisplayPort interface of the above driver/device.
*/
- pData->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
- if (!pData->pUpPort)
+ pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
+ if (!pThis->pUpPort)
{
AssertMsgFailed(("Configuration error: No display port interface above!\n"));
return VERR_PDM_MISSING_INTERFACE_ABOVE;
}
#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
- pData->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
- if (!pData->pVBVACallbacks)
+ pThis->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
+ if (!pThis->pVBVACallbacks)
{
AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
return VERR_PDM_MISSING_INTERFACE_ABOVE;
@@ -4341,24 +5088,36 @@ DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint
AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
return rc;
}
- pData->pDisplay = (Display *)pv; /** @todo Check this cast! */
- pData->pDisplay->mpDrv = pData;
-
+ Display *pDisplay = (Display *)pv; /** @todo Check this cast! */
+ pThis->pDisplay = pDisplay;
+ pThis->pDisplay->mpDrv = pThis;
/*
* Update our display information according to the framebuffer
*/
- pData->pDisplay->updateDisplayData();
+ pDisplay->updateDisplayData();
/*
* Start periodic screen refreshes
*/
- pData->pUpPort->pfnSetRefreshRate(pData->pUpPort, 20);
+ pThis->pUpPort->pfnSetRefreshRate(pThis->pUpPort, 20);
#ifdef VBOX_WITH_CRHGSMI
- pData->pDisplay->setupCrHgsmiData();
+ pDisplay->setupCrHgsmiData();
#endif
- return VINF_SUCCESS;
+#ifdef VBOX_WITH_VPX
+ ComPtr<IMachine> pMachine = pDisplay->mParent->machine();
+ BOOL fEnabled = false;
+ HRESULT hrc = pMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);
+ AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
+ if (fEnabled)
+ {
+ rc = pDisplay->VideoCaptureStart();
+ fireVideoCaptureChangedEvent(pDisplay->mParent->getEventSource());
+ }
+#endif
+
+ return rc;
}
diff --git a/src/VBox/Main/src-client/EbmlWriter.cpp b/src/VBox/Main/src-client/EbmlWriter.cpp
new file mode 100644
index 00000000..552e9d36
--- /dev/null
+++ b/src/VBox/Main/src-client/EbmlWriter.cpp
@@ -0,0 +1,504 @@
+/* $Id: EbmlWriter.cpp $ */
+/** @file
+ * EbmlWriter.cpp - EBML writer + WebM container
+ */
+
+/*
+ * Copyright (C) 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;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 2010 The WebM project authors. All Rights Reserved.
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "EbmlWriter.h"
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <VBox/log.h>
+
+static int ebml_Write(EbmlGlobal *glob, const void *pv, size_t cb)
+{
+ return RTFileWrite(glob->file, pv, cb, NULL);
+}
+
+static int ebml_WriteU8(EbmlGlobal *glob, uint8_t u8)
+{
+ return ebml_Write(glob, &u8, 1);
+}
+
+static int ebml_WriteU16(EbmlGlobal *glob, uint16_t u16)
+{
+ return ebml_Write(glob, &u16, 2);
+}
+
+static int ebml_WriteU32(EbmlGlobal *glob, uint32_t u32)
+{
+ return ebml_Write(glob, &u32, 4);
+}
+
+static int ebml_WriteU64(EbmlGlobal *glob, uint64_t u64)
+{
+ return ebml_Write(glob, &u64, 8);
+}
+
+static int ebml_Serialize(EbmlGlobal *glob, const uint8_t *pb, size_t cb)
+{
+ for (; cb; cb--)
+ {
+ int rc = ebml_WriteU8(glob, pb[cb-1]);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+static int ebml_WriteID(EbmlGlobal *glob, uint32_t class_id)
+{
+ int rc;
+ if (class_id >= 0x01000000)
+ rc = ebml_WriteU32(glob, RT_H2BE_U32(class_id));
+ else if (class_id >= 0x00010000)
+ rc = ebml_Serialize(glob, (uint8_t*)&class_id, 3);
+ else if (class_id >= 0x00000100)
+ rc = ebml_WriteU16(glob, RT_H2BE_U16((uint16_t)class_id));
+ else
+ rc = ebml_WriteU8(glob, (uint8_t)class_id);
+ return rc;
+}
+
+static int ebml_SerializeUnsigned32(EbmlGlobal *glob, uint32_t class_id, uint32_t ui)
+{
+ int rc = ebml_WriteID(glob, class_id);
+ rc = ebml_WriteU8(glob, 4 | 0x80);
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteU32(glob, RT_H2BE_U32(ui));
+ return rc;
+}
+
+static int ebml_StartSubElement(EbmlGlobal *glob, uint64_t *ebmlLoc, uint32_t class_id)
+{
+ // todo this is always taking 8 bytes, this may need later optimization
+ // this is a key that says lenght unknown
+ uint64_t unknownLen = UINT64_C(0x01FFFFFFFFFFFFFF);
+
+ ebml_WriteID(glob, class_id);
+ *ebmlLoc = RTFileTell(glob->file);
+ return ebml_WriteU64(glob, RT_H2BE_U64(unknownLen));
+}
+
+static int ebml_EndSubElement(EbmlGlobal *glob, uint64_t ebmlLoc)
+{
+ /* Save the current file pointer */
+ uint64_t pos = RTFileTell(glob->file);
+
+ /* Calculate the size of this element */
+ uint64_t size = pos - ebmlLoc - 8;
+ size |= UINT64_C(0x0100000000000000);
+
+ /* Seek back to the beginning of the element and write the new size */
+ RTFileSeek(glob->file, ebmlLoc, RTFILE_SEEK_BEGIN, NULL);
+ int rc = ebml_WriteU64(glob, RT_H2BE_U64(size));
+
+ /* Reset the file pointer */
+ RTFileSeek(glob->file, pos, RTFILE_SEEK_BEGIN, NULL);
+
+ return rc;
+}
+
+static int ebml_WriteLen(EbmlGlobal *glob, uint64_t val)
+{
+ //TODO check and make sure we are not > than 0x0100000000000000LLU
+ size_t size = 8;
+ uint64_t minVal = UINT64_C(0x00000000000000ff); //mask to compare for byte size
+
+ for (size = 1; size < 8; size ++)
+ {
+ if (val < minVal)
+ break;
+
+ minVal = (minVal << 7);
+ }
+
+ val |= (UINT64_C(0x000000000000080) << ((size - 1) * 7));
+
+ return ebml_Serialize(glob, (uint8_t *)&val, size);
+}
+
+static int ebml_WriteString(EbmlGlobal *glob, const char *str)
+{
+ const size_t cb = strlen(str);
+ int rc = ebml_WriteLen(glob, cb);
+ //TODO: it's not clear from the spec whether the nul terminator
+ //should be serialized too. For now we omit the null terminator.
+ if (RT_SUCCESS(rc))
+ rc = ebml_Write(glob, str, cb);
+ return rc;
+}
+
+int Ebml_SerializeUnsigned64(EbmlGlobal *glob, uint32_t class_id, uint64_t ui)
+{
+ int rc = ebml_WriteID(glob, class_id);
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteU8(glob, 8 | 0x80);
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteU64(glob, RT_H2BE_U64(ui));
+ return rc;
+}
+
+int Ebml_SerializeUnsigned(EbmlGlobal *glob, uint32_t class_id, uint32_t ui)
+{
+ int rc = ebml_WriteID(glob, class_id);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint32_t minVal = 0x7fLU; //mask to compare for byte size
+ size_t size = 8; //size in bytes to output
+ for (size = 1; size < 4; size ++)
+ {
+ if (ui < minVal)
+ break;
+
+ minVal <<= 7;
+ }
+
+ rc = ebml_WriteU8(glob, 0x80 | size);
+ if (RT_SUCCESS(rc))
+ rc = ebml_Serialize(glob, (uint8_t*)&ui, size);
+ return rc;
+}
+
+//TODO: perhaps this is a poor name for this id serializer helper function
+int Ebml_SerializeBinary(EbmlGlobal *glob, uint32_t class_id, uint32_t bin)
+{
+ int size;
+ for (size = 4; size > 1; size--)
+ {
+ if (bin & 0x000000ff << ((size-1) * 8))
+ break;
+ }
+ int rc = ebml_WriteID(glob, class_id);
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteLen(glob, size);
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteID(glob, bin);
+ return rc;
+}
+
+int Ebml_SerializeFloat(EbmlGlobal *glob, uint32_t class_id, double d)
+{
+ int rc = ebml_WriteID(glob, class_id);
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteU8(glob, 0x80 | 8);
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteU64(glob, RT_H2BE_U64(*(uint64_t*)&d));
+ return rc;
+}
+
+int Ebml_SerializeString(EbmlGlobal *glob, uint32_t class_id, const char *s)
+{
+ int rc = ebml_WriteID(glob, class_id);
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteString(glob, s);
+ return rc;
+}
+
+int Ebml_WriteWebMSeekElement(EbmlGlobal *ebml, uint32_t id, uint64_t pos)
+{
+ uint64_t offset = pos - ebml->position_reference;
+ uint64_t start;
+ int rc = ebml_StartSubElement(ebml, &start, Seek);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeBinary(ebml, SeekID, id);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned64(ebml, SeekPosition, offset);
+ if (RT_SUCCESS(rc))
+ rc = ebml_EndSubElement(ebml, start);
+ return rc;
+}
+
+int Ebml_WriteWebMSeekInfo(EbmlGlobal *ebml)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Save the current file pointer */
+ uint64_t pos = RTFileTell(ebml->file);
+
+ if (ebml->seek_info_pos)
+ rc = RTFileSeek(ebml->file, ebml->seek_info_pos, RTFILE_SEEK_BEGIN, NULL);
+ else
+ ebml->seek_info_pos = pos;
+
+ {
+ uint64_t start;
+
+ if (RT_SUCCESS(rc))
+ rc = ebml_StartSubElement(ebml, &start, SeekHead);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_WriteWebMSeekElement(ebml, Tracks, ebml->track_pos);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_WriteWebMSeekElement(ebml, Cues, ebml->cue_pos);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_WriteWebMSeekElement(ebml, Info, ebml->segment_info_pos);
+ if (RT_SUCCESS(rc))
+ rc = ebml_EndSubElement(ebml, start);
+ }
+ {
+ //segment info
+ uint64_t startInfo;
+ uint64_t frame_time;
+
+ frame_time = (uint64_t)1000 * ebml->framerate.den / ebml->framerate.num;
+ ebml->segment_info_pos = RTFileTell(ebml->file);
+ if (RT_SUCCESS(rc))
+ rc = ebml_StartSubElement(ebml, &startInfo, Info);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeFloat(ebml, Segment_Duration,
+ (double)(ebml->last_pts_ms + frame_time));
+ char szVersion[64];
+ RTStrPrintf(szVersion, sizeof(szVersion), "vpxenc%",
+ ebml->debug ? vpx_codec_version_str() : "");
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeString(ebml, MuxingApp, szVersion);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeString(ebml, WritingApp, szVersion);
+ if (RT_SUCCESS(rc))
+ rc = ebml_EndSubElement(ebml, startInfo);
+ }
+ return rc;
+}
+
+int Ebml_WriteWebMFileHeader(EbmlGlobal *glob,
+ const vpx_codec_enc_cfg_t *cfg,
+ const struct vpx_rational *fps)
+{
+ int rc = VINF_SUCCESS;
+ {
+ uint64_t start;
+ rc = ebml_StartSubElement(glob, &start, EBML);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, EBMLVersion, 1);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeString(glob, DocType, "webm");
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, DocTypeVersion, 2);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2);
+ if (RT_SUCCESS(rc))
+ rc = ebml_EndSubElement(glob, start);
+ }
+ {
+ if (RT_SUCCESS(rc))
+ rc = ebml_StartSubElement(glob, &glob->startSegment, Segment);
+ glob->position_reference = RTFileTell(glob->file);
+ glob->framerate = *fps;
+ if (RT_SUCCESS(rc))
+ rc = Ebml_WriteWebMSeekInfo(glob);
+ {
+ uint64_t trackStart;
+ glob->track_pos = RTFileTell(glob->file);
+ if (RT_SUCCESS(rc))
+ rc = ebml_StartSubElement(glob, &trackStart, Tracks);
+ {
+ uint32_t trackNumber = 1;
+ uint32_t trackID = 0;
+
+ uint64_t start;
+ if (RT_SUCCESS(rc))
+ rc = ebml_StartSubElement(glob, &start, TrackEntry);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber);
+ glob->track_id_pos = RTFileTell(glob->file);
+ ebml_SerializeUnsigned32(glob, TrackUID, trackID);
+ Ebml_SerializeUnsigned(glob, TrackType, 1); //video is always 1
+ Ebml_SerializeString(glob, CodecID, "V_VP8");
+ {
+ uint64_t videoStart;
+ if (RT_SUCCESS(rc))
+ rc = ebml_StartSubElement(glob, &videoStart, Video);
+ uint32_t pixelWidth = cfg->g_w;
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth);
+ uint32_t pixelHeight = cfg->g_h;
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight);
+ double frameRate = (double)fps->num / (double)fps->den;
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeFloat(glob, FrameRate, frameRate);
+ if (RT_SUCCESS(rc))
+ rc = ebml_EndSubElement(glob, videoStart);
+ }
+ if (RT_SUCCESS(rc))
+ rc = ebml_EndSubElement(glob, start); //Track Entry
+ }
+ if (RT_SUCCESS(rc))
+ rc = ebml_EndSubElement(glob, trackStart);
+ }
+ // segment element is open
+ }
+ return rc;
+}
+
+int Ebml_WriteWebMBlock(EbmlGlobal *glob,
+ const vpx_codec_enc_cfg_t *cfg,
+ const vpx_codec_cx_pkt_t *pkt)
+{
+ uint16_t block_timecode = 0;
+ int64_t pts_ms;
+ int start_cluster = 0;
+ int rc = VINF_SUCCESS;
+
+ /* Calculate the PTS of this frame in milliseconds */
+ pts_ms = pkt->data.frame.pts * 1000
+ * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den;
+ if (pts_ms <= glob->last_pts_ms)
+ pts_ms = glob->last_pts_ms + 1;
+ glob->last_pts_ms = pts_ms;
+
+ /* Calculate the relative time of this block */
+ if (pts_ms - glob->cluster_timecode > 65536)
+ start_cluster = 1;
+ else
+ block_timecode = (uint16_t)(pts_ms - glob->cluster_timecode);
+
+ int fKeyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY);
+ if (start_cluster || fKeyframe)
+ {
+ if (glob->cluster_open)
+ rc = ebml_EndSubElement(glob, glob->startCluster);
+
+ /* Open the new cluster */
+ block_timecode = 0;
+ glob->cluster_open = 1;
+ glob->cluster_timecode = (uint32_t)pts_ms;
+ glob->cluster_pos = RTFileTell(glob->file);
+ if (RT_SUCCESS(rc))
+ rc = ebml_StartSubElement(glob, &glob->startCluster, Cluster); //cluster
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode);
+
+ /* Save a cue point if this is a keyframe. */
+ if (fKeyframe)
+ {
+ struct cue_entry *cue;
+
+ glob->cue_list = (cue_entry*)RTMemRealloc(glob->cue_list, (glob->cues+1) * sizeof(cue_entry));
+ cue = &glob->cue_list[glob->cues];
+ cue->time = glob->cluster_timecode;
+ cue->loc = glob->cluster_pos;
+ glob->cues++;
+ }
+ }
+
+ /* Write the Simple Block */
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteID(glob, SimpleBlock);
+
+ uint32_t block_length = pkt->data.frame.sz + 4;
+ block_length |= 0x10000000;
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteU32(glob, RT_H2BE_U32(block_length));
+
+ uint8_t track_number = 0x80 | 1;
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteU8(glob, track_number);
+
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteU16(glob, RT_H2BE_U16(block_timecode));
+
+ uint8_t flags = 0;
+ if (fKeyframe)
+ flags |= 0x80;
+ if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
+ flags |= 0x08;
+ if (RT_SUCCESS(rc))
+ rc = ebml_WriteU8(glob, flags);
+
+ if (RT_SUCCESS(rc))
+ rc = ebml_Write(glob, pkt->data.frame.buf, pkt->data.frame.sz);
+
+ return rc;
+}
+
+int Ebml_WriteWebMFileFooter(EbmlGlobal *glob, long hash)
+{
+ int rc = VINF_SUCCESS;
+ if (glob->cluster_open)
+ rc = ebml_EndSubElement(glob, glob->startCluster);
+
+ {
+ uint64_t start;
+
+ glob->cue_pos = RTFileTell(glob->file);
+ if (RT_SUCCESS(rc))
+ rc = ebml_StartSubElement(glob, &start, Cues);
+ for (unsigned i = 0; i < glob->cues; i++)
+ {
+ struct cue_entry *cue = &glob->cue_list[i];
+ uint64_t startSub;
+
+ if (RT_SUCCESS(rc))
+ rc = ebml_StartSubElement(glob, &startSub, CuePoint);
+ {
+ uint64_t startSubsub;
+
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, CueTime, cue->time);
+ if (RT_SUCCESS(rc))
+ rc = ebml_StartSubElement(glob, &startSubsub, CueTrackPositions);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned(glob, CueTrack, 1);
+ if (RT_SUCCESS(rc))
+ rc = Ebml_SerializeUnsigned64(glob, CueClusterPosition,
+ cue->loc - glob->position_reference);
+ //Ebml_SerializeUnsigned(glob, CueBlockNumber, cue->blockNumber);
+ if (RT_SUCCESS(rc))
+ rc = ebml_EndSubElement(glob, startSubsub);
+ }
+ if (RT_SUCCESS(rc))
+ rc = ebml_EndSubElement(glob, startSub);
+ }
+ if (RT_SUCCESS(rc))
+ rc = ebml_EndSubElement(glob, start);
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = ebml_EndSubElement(glob, glob->startSegment);
+
+ /* Patch up the seek info block */
+ if (RT_SUCCESS(rc))
+ rc = Ebml_WriteWebMSeekInfo(glob);
+
+ /* Patch up the track id */
+ if (RT_SUCCESS(rc))
+ rc = RTFileSeek(glob->file, glob->track_id_pos, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ rc = ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash);
+
+ if (RT_SUCCESS(rc))
+ rc = RTFileSeek(glob->file, 0, RTFILE_SEEK_END, NULL);
+ return rc;
+}
diff --git a/src/VBox/Main/src-client/EbmlWriter.h b/src/VBox/Main/src-client/EbmlWriter.h
new file mode 100644
index 00000000..e24740c1
--- /dev/null
+++ b/src/VBox/Main/src-client/EbmlWriter.h
@@ -0,0 +1,291 @@
+/* $Id: EbmlWriter.h $ */
+/** @file
+ * EbmlWriter.h - EBML writer + WebM container.
+ */
+
+/*
+ * Copyright (C) 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;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 2010 The WebM project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef ____EBMLWRITER
+#define ____EBMLWRITER
+
+#include <iprt/file.h>
+#include <vpx/vpx_encoder.h>
+
+enum mkv
+{
+ EBML = 0x1A45DFA3,
+ EBMLVersion = 0x4286,
+ EBMLReadVersion = 0x42F7,
+ EBMLMaxIDLength = 0x42F2,
+ EBMLMaxSizeLength = 0x42F3,
+ DocType = 0x4282,
+ DocTypeVersion = 0x4287,
+ DocTypeReadVersion = 0x4285,
+// CRC_32 = 0xBF,
+ Void = 0xEC,
+ SignatureSlot = 0x1B538667,
+ SignatureAlgo = 0x7E8A,
+ SignatureHash = 0x7E9A,
+ SignaturePublicKey = 0x7EA5,
+ Signature = 0x7EB5,
+ SignatureElements = 0x7E5B,
+ SignatureElementList = 0x7E7B,
+ SignedElement = 0x6532,
+ //segment
+ Segment = 0x18538067,
+ //Meta Seek Information
+ SeekHead = 0x114D9B74,
+ Seek = 0x4DBB,
+ SeekID = 0x53AB,
+ SeekPosition = 0x53AC,
+ //Segment Information
+ Info = 0x1549A966,
+// SegmentUID = 0x73A4,
+// SegmentFilename = 0x7384,
+// PrevUID = 0x3CB923,
+// PrevFilename = 0x3C83AB,
+// NextUID = 0x3EB923,
+// NextFilename = 0x3E83BB,
+// SegmentFamily = 0x4444,
+// ChapterTranslate = 0x6924,
+// ChapterTranslateEditionUID = 0x69FC,
+// ChapterTranslateCodec = 0x69BF,
+// ChapterTranslateID = 0x69A5,
+ TimecodeScale = 0x2AD7B1,
+ Segment_Duration = 0x4489,
+ DateUTC = 0x4461,
+// Title = 0x7BA9,
+ MuxingApp = 0x4D80,
+ WritingApp = 0x5741,
+ //Cluster
+ Cluster = 0x1F43B675,
+ Timecode = 0xE7,
+// SilentTracks = 0x5854,
+// SilentTrackNumber = 0x58D7,
+// Position = 0xA7,
+ PrevSize = 0xAB,
+ BlockGroup = 0xA0,
+ Block = 0xA1,
+// BlockVirtual = 0xA2,
+// BlockAdditions = 0x75A1,
+// BlockMore = 0xA6,
+// BlockAddID = 0xEE,
+// BlockAdditional = 0xA5,
+ BlockDuration = 0x9B,
+// ReferencePriority = 0xFA,
+ ReferenceBlock = 0xFB,
+// ReferenceVirtual = 0xFD,
+// CodecState = 0xA4,
+// Slices = 0x8E,
+// TimeSlice = 0xE8,
+ LaceNumber = 0xCC,
+// FrameNumber = 0xCD,
+// BlockAdditionID = 0xCB,
+// MkvDelay = 0xCE,
+// Cluster_Duration = 0xCF,
+ SimpleBlock = 0xA3,
+// EncryptedBlock = 0xAF,
+ //Track
+ Tracks = 0x1654AE6B,
+ TrackEntry = 0xAE,
+ TrackNumber = 0xD7,
+ TrackUID = 0x73C5,
+ TrackType = 0x83,
+ FlagEnabled = 0xB9,
+ FlagDefault = 0x88,
+ FlagForced = 0x55AA,
+ FlagLacing = 0x9C,
+// MinCache = 0x6DE7,
+// MaxCache = 0x6DF8,
+ DefaultDuration = 0x23E383,
+// TrackTimecodeScale = 0x23314F,
+// TrackOffset = 0x537F,
+// MaxBlockAdditionID = 0x55EE,
+ Name = 0x536E,
+ Language = 0x22B59C,
+ CodecID = 0x86,
+ CodecPrivate = 0x63A2,
+ CodecName = 0x258688,
+// AttachmentLink = 0x7446,
+// CodecSettings = 0x3A9697,
+// CodecInfoURL = 0x3B4040,
+// CodecDownloadURL = 0x26B240,
+// CodecDecodeAll = 0xAA,
+// TrackOverlay = 0x6FAB,
+// TrackTranslate = 0x6624,
+// TrackTranslateEditionUID = 0x66FC,
+// TrackTranslateCodec = 0x66BF,
+// TrackTranslateTrackID = 0x66A5,
+ //video
+ Video = 0xE0,
+ FlagInterlaced = 0x9A,
+// StereoMode = 0x53B8,
+ PixelWidth = 0xB0,
+ PixelHeight = 0xBA,
+ PixelCropBottom = 0x54AA,
+ PixelCropTop = 0x54BB,
+ PixelCropLeft = 0x54CC,
+ PixelCropRight = 0x54DD,
+ DisplayWidth = 0x54B0,
+ DisplayHeight = 0x54BA,
+ DisplayUnit = 0x54B2,
+ AspectRatioType = 0x54B3,
+// ColourSpace = 0x2EB524,
+// GammaValue = 0x2FB523,
+ FrameRate = 0x2383E3,
+ //end video
+ //audio
+ Audio = 0xE1,
+ SamplingFrequency = 0xB5,
+ OutputSamplingFrequency = 0x78B5,
+ Channels = 0x9F,
+// ChannelPositions = 0x7D7B,
+ BitDepth = 0x6264,
+ //end audio
+ //content encoding
+// ContentEncodings = 0x6d80,
+// ContentEncoding = 0x6240,
+// ContentEncodingOrder = 0x5031,
+// ContentEncodingScope = 0x5032,
+// ContentEncodingType = 0x5033,
+// ContentCompression = 0x5034,
+// ContentCompAlgo = 0x4254,
+// ContentCompSettings = 0x4255,
+// ContentEncryption = 0x5035,
+// ContentEncAlgo = 0x47e1,
+// ContentEncKeyID = 0x47e2,
+// ContentSignature = 0x47e3,
+// ContentSigKeyID = 0x47e4,
+// ContentSigAlgo = 0x47e5,
+// ContentSigHashAlgo = 0x47e6,
+ //end content encoding
+ //Cueing Data
+ Cues = 0x1C53BB6B,
+ CuePoint = 0xBB,
+ CueTime = 0xB3,
+ CueTrackPositions = 0xB7,
+ CueTrack = 0xF7,
+ CueClusterPosition = 0xF1,
+ CueBlockNumber = 0x5378
+// CueCodecState = 0xEA,
+// CueReference = 0xDB,
+// CueRefTime = 0x96,
+// CueRefCluster = 0x97,
+// CueRefNumber = 0x535F,
+// CueRefCodecState = 0xEB,
+ //Attachment
+// Attachments = 0x1941A469,
+// AttachedFile = 0x61A7,
+// FileDescription = 0x467E,
+// FileName = 0x466E,
+// FileMimeType = 0x4660,
+// FileData = 0x465C,
+// FileUID = 0x46AE,
+// FileReferral = 0x4675,
+ //Chapters
+// Chapters = 0x1043A770,
+// EditionEntry = 0x45B9,
+// EditionUID = 0x45BC,
+// EditionFlagHidden = 0x45BD,
+// EditionFlagDefault = 0x45DB,
+// EditionFlagOrdered = 0x45DD,
+// ChapterAtom = 0xB6,
+// ChapterUID = 0x73C4,
+// ChapterTimeStart = 0x91,
+// ChapterTimeEnd = 0x92,
+// ChapterFlagHidden = 0x98,
+// ChapterFlagEnabled = 0x4598,
+// ChapterSegmentUID = 0x6E67,
+// ChapterSegmentEditionUID = 0x6EBC,
+// ChapterPhysicalEquiv = 0x63C3,
+// ChapterTrack = 0x8F,
+// ChapterTrackNumber = 0x89,
+// ChapterDisplay = 0x80,
+// ChapString = 0x85,
+// ChapLanguage = 0x437C,
+// ChapCountry = 0x437E,
+// ChapProcess = 0x6944,
+// ChapProcessCodecID = 0x6955,
+// ChapProcessPrivate = 0x450D,
+// ChapProcessCommand = 0x6911,
+// ChapProcessTime = 0x6922,
+// ChapProcessData = 0x6933,
+ //Tagging
+// Tags = 0x1254C367,
+// Tag = 0x7373,
+// Targets = 0x63C0,
+// TargetTypeValue = 0x68CA,
+// TargetType = 0x63CA,
+// Tagging_TrackUID = 0x63C5,
+// Tagging_EditionUID = 0x63C9,
+// Tagging_ChapterUID = 0x63C4,
+// AttachmentUID = 0x63C6,
+// SimpleTag = 0x67C8,
+// TagName = 0x45A3,
+// TagLanguage = 0x447A,
+// TagDefault = 0x4484,
+// TagString = 0x4487,
+// TagBinary = 0x4485,
+};
+
+struct cue_entry
+{
+ uint32_t time;
+ uint64_t loc;
+};
+
+struct EbmlGlobal
+{
+ int debug;
+ RTFILE file;
+ int64_t last_pts_ms;
+ vpx_rational_t framerate;
+
+ uint64_t position_reference;
+ uint64_t seek_info_pos;
+ uint64_t segment_info_pos;
+ uint64_t track_pos;
+ uint64_t cue_pos;
+ uint64_t cluster_pos;
+
+ uint64_t track_id_pos;
+
+ uint64_t startSegment;
+ uint64_t startCluster;
+
+ uint32_t cluster_timecode;
+ int cluster_open;
+
+ struct cue_entry *cue_list;
+ unsigned int cues;
+};
+
+int Ebml_WriteWebMSeekElement(EbmlGlobal *ebml, uint32_t id, uint64_t pos);
+int Ebml_WriteWebMSeekInfo(EbmlGlobal *ebml);
+int Ebml_WriteWebMFileHeader(EbmlGlobal *glob, const vpx_codec_enc_cfg_t *cfg, const struct vpx_rational *fps);
+int Ebml_WriteWebMBlock(EbmlGlobal *glob, const vpx_codec_enc_cfg_t *cfg, const vpx_codec_cx_pkt_t *pkt);
+int Ebml_WriteWebMFileFooter(EbmlGlobal *glob, long hash);
+
+#endif
diff --git a/src/VBox/Main/src-client/EmulatedUSBImpl.cpp b/src/VBox/Main/src-client/EmulatedUSBImpl.cpp
new file mode 100644
index 00000000..8c8aa8fd
--- /dev/null
+++ b/src/VBox/Main/src-client/EmulatedUSBImpl.cpp
@@ -0,0 +1,692 @@
+/* $Id: EmulatedUSBImpl.cpp $ */
+/** @file
+ *
+ * Emulated USB manager implementation.
+ */
+
+/*
+ * Copyright (C) 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;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#define LOG_GROUP_MAIN_OVERRIDE LOG_GROUP_MAIN_EMULATEDUSB
+
+#include "EmulatedUSBImpl.h"
+#include "ConsoleImpl.h"
+#include "Logging.h"
+
+#include <VBox/vmm/pdmusb.h>
+
+
+/*
+ * Emulated USB webcam device instance.
+ */
+typedef std::map <Utf8Str, Utf8Str> EUSBSettingsMap;
+
+typedef enum EUSBDEVICESTATUS
+{
+ EUSBDEVICE_CREATED,
+ EUSBDEVICE_ATTACHING,
+ EUSBDEVICE_ATTACHED
+} EUSBDEVICESTATUS;
+
+class EUSBWEBCAM /* : public EUSBDEVICE */
+{
+ private:
+ int32_t volatile mcRefs;
+
+ EmulatedUSB *mpEmulatedUSB;
+
+ RTUUID mUuid;
+ char mszUuid[RTUUID_STR_LENGTH];
+
+ Utf8Str mPath;
+ Utf8Str mSettings;
+
+ EUSBSettingsMap mDevSettings;
+ EUSBSettingsMap mDrvSettings;
+
+ void *mpvObject;
+
+ static DECLCALLBACK(int) emulatedWebcamAttach(PUVM pUVM, EUSBWEBCAM *pThis, const char *pszDriver);
+ static DECLCALLBACK(int) emulatedWebcamDetach(PUVM pUVM, EUSBWEBCAM *pThis);
+
+ HRESULT settingsParse(void);
+
+ ~EUSBWEBCAM()
+ {
+ }
+
+ public:
+ EUSBWEBCAM()
+ :
+ mcRefs(1),
+ mpEmulatedUSB(NULL),
+ mpvObject(NULL),
+ enmStatus(EUSBDEVICE_CREATED)
+ {
+ RT_ZERO(mUuid);
+ RT_ZERO(mszUuid);
+ }
+
+ int32_t AddRef(void)
+ {
+ return ASMAtomicIncS32(&mcRefs);
+ }
+
+ void Release(void)
+ {
+ int32_t c = ASMAtomicDecS32(&mcRefs);
+ if (c == 0)
+ {
+ delete this;
+ }
+ }
+
+ HRESULT Initialize(Console *pConsole,
+ EmulatedUSB *pEmulatedUSB,
+ const com::Utf8Str *aPath,
+ const com::Utf8Str *aSettings,
+ void *pvObject);
+ HRESULT Attach(Console *pConsole,
+ PUVM pUVM,
+ const char *pszDriver);
+ HRESULT Detach(Console *pConsole,
+ PUVM pUVM);
+
+ bool HasId(const char *pszId) { return RTStrCmp(pszId, mszUuid) == 0;}
+
+ EUSBDEVICESTATUS enmStatus;
+};
+
+static int emulatedWebcamInsertSettings(PCFGMNODE pConfig, EUSBSettingsMap *pSettings)
+{
+ int rc = VINF_SUCCESS;
+
+ EUSBSettingsMap::const_iterator it;
+ for (it = pSettings->begin(); it != pSettings->end(); ++it)
+ {
+ /* Convert some well known settings for backward compatibility. */
+ if ( RTStrCmp(it->first.c_str(), "MaxPayloadTransferSize") == 0
+ || RTStrCmp(it->first.c_str(), "MaxFramerate") == 0)
+ {
+ uint32_t u32 = 0;
+ rc = RTStrToUInt32Full(it->second.c_str(), 10, &u32);
+ if (rc == VINF_SUCCESS)
+ {
+ rc = CFGMR3InsertInteger(pConfig, it->first.c_str(), u32);
+ }
+ else
+ {
+ if (RT_SUCCESS(rc)) /* VWRN_* */
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ }
+ }
+ else
+ {
+ rc = CFGMR3InsertString(pConfig, it->first.c_str(), it->second.c_str());
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/* static */ DECLCALLBACK(int) EUSBWEBCAM::emulatedWebcamAttach(PUVM pUVM, EUSBWEBCAM *pThis, const char *pszDriver)
+{
+ PCFGMNODE pInstance = CFGMR3CreateTree(pUVM);
+ PCFGMNODE pConfig;
+ CFGMR3InsertNode(pInstance, "Config", &pConfig);
+ int rc = emulatedWebcamInsertSettings(pConfig, &pThis->mDevSettings);
+ if (RT_FAILURE(rc))
+ return rc;
+ PCFGMNODE pEUSB;
+ CFGMR3InsertNode(pConfig, "EmulatedUSB", &pEUSB);
+ CFGMR3InsertString(pEUSB, "Id", pThis->mszUuid);
+ CFGMR3InsertInteger(pEUSB, "pfnCallback", (uintptr_t)EmulatedUSB::eusbCallback);
+ CFGMR3InsertInteger(pEUSB, "pvCallback", (uintptr_t)pThis->mpEmulatedUSB);
+
+ PCFGMNODE pLunL0;
+ CFGMR3InsertNode(pInstance, "LUN#0", &pLunL0);
+ CFGMR3InsertString(pLunL0, "Driver", pszDriver);
+ CFGMR3InsertNode(pLunL0, "Config", &pConfig);
+ CFGMR3InsertString(pConfig, "DevicePath", pThis->mPath.c_str());
+ CFGMR3InsertInteger(pConfig, "Object", (uintptr_t)pThis->mpvObject);
+ rc = emulatedWebcamInsertSettings(pConfig, &pThis->mDrvSettings);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* pInstance will be used by PDM and deallocated on error. */
+ rc = PDMR3UsbCreateEmulatedDevice(pUVM, "Webcam", pInstance, &pThis->mUuid);
+ LogRelFlowFunc(("PDMR3UsbCreateEmulatedDevice %Rrc\n", rc));
+ return rc;
+}
+
+/* static */ DECLCALLBACK(int) EUSBWEBCAM::emulatedWebcamDetach(PUVM pUVM, EUSBWEBCAM *pThis)
+{
+ return PDMR3UsbDetachDevice(pUVM, &pThis->mUuid);
+}
+
+HRESULT EUSBWEBCAM::Initialize(Console *pConsole,
+ EmulatedUSB *pEmulatedUSB,
+ const com::Utf8Str *aPath,
+ const com::Utf8Str *aSettings,
+ void *pvObject)
+{
+ HRESULT hrc = S_OK;
+
+ int vrc = RTUuidCreate(&mUuid);
+ if (RT_SUCCESS(vrc))
+ {
+ RTStrPrintf(mszUuid, sizeof(mszUuid), "%RTuuid", &mUuid);
+ hrc = mPath.assignEx(*aPath);
+ if (SUCCEEDED(hrc))
+ {
+ hrc = mSettings.assignEx(*aSettings);
+ }
+
+ if (SUCCEEDED(hrc))
+ {
+ hrc = settingsParse();
+
+ if (SUCCEEDED(hrc))
+ {
+ mpEmulatedUSB = pEmulatedUSB;
+ mpvObject = pvObject;
+ }
+ }
+ }
+
+ if (SUCCEEDED(hrc) && RT_FAILURE(vrc))
+ {
+ LogFlowThisFunc(("%Rrc\n", vrc));
+ hrc = pConsole->setError(VBOX_E_IPRT_ERROR,
+ "Init emulated USB webcam (%Rrc)", vrc);
+ }
+
+ return hrc;
+}
+
+HRESULT EUSBWEBCAM::settingsParse(void)
+{
+ HRESULT hr = S_OK;
+
+ /* Parse mSettings string:
+ * "[dev:|drv:]Name1=Value1;[dev:|drv:]Name2=Value2"
+ */
+ char *pszSrc = mSettings.mutableRaw();
+
+ if (pszSrc)
+ {
+ while (*pszSrc)
+ {
+ /* Does the setting belong to device of driver. Default is both. */
+ bool fDev = true;
+ bool fDrv = true;
+ if (RTStrNICmp(pszSrc, "drv:", strlen("drv:")) == 0)
+ {
+ pszSrc += strlen("drv:");
+ fDev = false;
+ }
+ else if (RTStrNICmp(pszSrc, "dev:", strlen("dev:")) == 0)
+ {
+ pszSrc += strlen("dev:");
+ fDrv = false;
+ }
+
+ char *pszEq = RTStrStr(pszSrc, "=");
+ if (!pszEq)
+ {
+ hr = E_INVALIDARG;
+ break;
+ }
+
+ char *pszEnd = RTStrStr(pszEq, ";");
+ if (!pszEnd)
+ {
+ pszEnd = pszEq + strlen(pszEq);
+ }
+
+ *pszEq = 0;
+ char chEnd = *pszEnd;
+ *pszEnd = 0;
+
+ /* Empty strings not allowed. */
+ if (*pszSrc != 0 && pszEq[1] != 0)
+ {
+ if (fDev)
+ {
+ mDevSettings[pszSrc] = &pszEq[1];
+ }
+ if (fDrv)
+ {
+ mDrvSettings[pszSrc] = &pszEq[1];
+ }
+ }
+
+ *pszEq = '=';
+ *pszEnd = chEnd;
+
+ pszSrc = pszEnd;
+ if (*pszSrc == ';')
+ {
+ pszSrc++;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ EUSBSettingsMap::const_iterator it;
+ for (it = mDevSettings.begin(); it != mDevSettings.end(); ++it)
+ LogRelFlowFunc(("[dev:%s] = [%s]\n", it->first.c_str(), it->second.c_str()));
+ for (it = mDrvSettings.begin(); it != mDrvSettings.end(); ++it)
+ LogRelFlowFunc(("[drv:%s] = [%s]\n", it->first.c_str(), it->second.c_str()));
+ }
+ }
+
+ return hr;
+}
+
+HRESULT EUSBWEBCAM::Attach(Console *pConsole,
+ PUVM pUVM,
+ const char *pszDriver)
+{
+ HRESULT hrc = S_OK;
+
+ int vrc = VMR3ReqCallWaitU(pUVM, 0 /* idDstCpu (saved state, see #6232) */,
+ (PFNRT)emulatedWebcamAttach, 3,
+ pUVM, this, pszDriver);
+
+ if (SUCCEEDED(hrc) && RT_FAILURE(vrc))
+ {
+ LogFlowThisFunc(("%Rrc\n", vrc));
+ hrc = pConsole->setError(VBOX_E_IPRT_ERROR,
+ "Attach emulated USB webcam (%Rrc)", vrc);
+ }
+
+ return hrc;
+}
+
+HRESULT EUSBWEBCAM::Detach(Console *pConsole,
+ PUVM pUVM)
+{
+ HRESULT hrc = S_OK;
+
+ int vrc = VMR3ReqCallWaitU(pUVM, 0 /* idDstCpu (saved state, see #6232) */,
+ (PFNRT)emulatedWebcamDetach, 2,
+ pUVM, this);
+
+ if (SUCCEEDED(hrc) && RT_FAILURE(vrc))
+ {
+ LogFlowThisFunc(("%Rrc\n", vrc));
+ hrc = pConsole->setError(VBOX_E_IPRT_ERROR,
+ "Detach emulated USB webcam (%Rrc)", vrc);
+ }
+
+ return hrc;
+}
+
+
+/*
+ * EmulatedUSB implementation.
+ */
+DEFINE_EMPTY_CTOR_DTOR(EmulatedUSB)
+
+HRESULT EmulatedUSB::FinalConstruct()
+{
+ return BaseFinalConstruct();
+}
+
+void EmulatedUSB::FinalRelease()
+{
+ uninit();
+
+ BaseFinalRelease();
+}
+
+/*
+ * Initializes the instance.
+ *
+ * @param pConsole The owner.
+ */
+HRESULT EmulatedUSB::init(ComObjPtr<Console> pConsole)
+{
+ LogFlowThisFunc(("\n"));
+
+ ComAssertRet(!pConsole.isNull(), E_INVALIDARG);
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ m.pConsole = pConsole;
+
+ /* Confirm a successful initialization */
+ autoInitSpan.setSucceeded();
+
+ return S_OK;
+}
+
+/*
+ * Uninitializes the instance.
+ * Called either from FinalRelease() or by the parent when it gets destroyed.
+ */
+void EmulatedUSB::uninit()
+{
+ LogFlowThisFunc(("\n"));
+
+ m.pConsole.setNull();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ WebcamsMap::iterator it = m.webcams.begin();
+ while (it != m.webcams.end())
+ {
+ EUSBWEBCAM *p = it->second;
+ m.webcams.erase(it++);
+ p->Release();
+ }
+ alock.release();
+
+ /* Enclose the state transition Ready->InUninit->NotReady */
+ AutoUninitSpan autoUninitSpan(this);
+ if (autoUninitSpan.uninitDone())
+ return;
+}
+
+HRESULT EmulatedUSB::getWebcams(std::vector<com::Utf8Str> &aWebcams)
+{
+ HRESULT hrc = S_OK;
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ try
+ {
+ aWebcams.resize(m.webcams.size());
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+ catch (...)
+ {
+ hrc = E_FAIL;
+ }
+
+ if (SUCCEEDED(hrc))
+ {
+ size_t i;
+ WebcamsMap::const_iterator it;
+ for (i = 0, it = m.webcams.begin(); it != m.webcams.end(); ++it)
+ aWebcams[i++] = it->first;
+ }
+
+ return hrc;
+}
+
+static const Utf8Str s_pathDefault(".0");
+
+HRESULT EmulatedUSB::webcamAttach(const com::Utf8Str &aPath,
+ const com::Utf8Str &aSettings)
+{
+ return webcamAttachInternal(aPath, aSettings, "HostWebcam", NULL);
+}
+
+HRESULT EmulatedUSB::webcamAttachInternal(const com::Utf8Str &aPath,
+ const com::Utf8Str &aSettings,
+ const char *pszDriver,
+ void *pvObject)
+{
+ HRESULT hrc = S_OK;
+
+ const Utf8Str &path = aPath.isEmpty() || aPath == "."? s_pathDefault: aPath;
+
+ Console::SafeVMPtr ptrVM(m.pConsole);
+ if (ptrVM.isOk())
+ {
+ EUSBWEBCAM *p = new EUSBWEBCAM();
+ if (p)
+ {
+ hrc = p->Initialize(m.pConsole, this, &path, &aSettings, pvObject);
+ if (SUCCEEDED(hrc))
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ WebcamsMap::const_iterator it = m.webcams.find(path);
+ if (it == m.webcams.end())
+ {
+ p->AddRef();
+ try
+ {
+ m.webcams[path] = p;
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+ catch (...)
+ {
+ hrc = E_FAIL;
+ }
+ p->enmStatus = EUSBDEVICE_ATTACHING;
+ }
+ else
+ {
+ hrc = E_FAIL;
+ }
+ }
+
+ if (SUCCEEDED(hrc))
+ {
+ hrc = p->Attach(m.pConsole, ptrVM.rawUVM(), pszDriver);
+ }
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (SUCCEEDED(hrc))
+ {
+ p->enmStatus = EUSBDEVICE_ATTACHED;
+ }
+ else
+ {
+ if (p->enmStatus != EUSBDEVICE_CREATED)
+ {
+ m.webcams.erase(path);
+ }
+ }
+ alock.release();
+
+ p->Release();
+ }
+ else
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+ }
+ else
+ {
+ hrc = VBOX_E_INVALID_VM_STATE;
+ }
+
+ return hrc;
+}
+
+HRESULT EmulatedUSB::webcamDetach(const com::Utf8Str &aPath)
+{
+ return webcamDetachInternal(aPath);
+}
+
+HRESULT EmulatedUSB::webcamDetachInternal(const com::Utf8Str &aPath)
+{
+ HRESULT hrc = S_OK;
+
+ const Utf8Str &path = aPath.isEmpty() || aPath == "."? s_pathDefault: aPath;
+
+ Console::SafeVMPtr ptrVM(m.pConsole);
+ if (ptrVM.isOk())
+ {
+ EUSBWEBCAM *p = NULL;
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ WebcamsMap::iterator it = m.webcams.find(path);
+ if (it != m.webcams.end())
+ {
+ if (it->second->enmStatus == EUSBDEVICE_ATTACHED)
+ {
+ p = it->second;
+ m.webcams.erase(it);
+ }
+ }
+ alock.release();
+
+ if (p)
+ {
+ hrc = p->Detach(m.pConsole, ptrVM.rawUVM());
+ p->Release();
+ }
+ else
+ {
+ hrc = E_INVALIDARG;
+ }
+ }
+ else
+ {
+ hrc = VBOX_E_INVALID_VM_STATE;
+ }
+
+ return hrc;
+}
+
+/* static */ DECLCALLBACK(int) EmulatedUSB::eusbCallbackEMT(EmulatedUSB *pThis, char *pszId, uint32_t iEvent,
+ void *pvData, uint32_t cbData)
+{
+ LogRelFlowFunc(("id %s event %d, data %p %d\n", pszId, iEvent, pvData, cbData));
+
+ NOREF(cbData);
+
+ int rc = VINF_SUCCESS;
+ if (iEvent == 0)
+ {
+ com::Utf8Str path;
+ HRESULT hr = pThis->webcamPathFromId(&path, pszId);
+ if (SUCCEEDED(hr))
+ {
+ hr = pThis->webcamDetach(path);
+ if (FAILED(hr))
+ {
+ rc = VERR_INVALID_STATE;
+ }
+ }
+ else
+ {
+ rc = VERR_NOT_FOUND;
+ }
+ }
+ else
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ RTMemFree(pszId);
+ RTMemFree(pvData);
+
+ LogRelFlowFunc(("rc %Rrc\n", rc));
+ return rc;
+}
+
+/* static */ DECLCALLBACK(int) EmulatedUSB::eusbCallback(void *pv, const char *pszId, uint32_t iEvent,
+ const void *pvData, uint32_t cbData)
+{
+ /* Make a copy of parameters, forward to EMT and leave the callback to not hold any lock in the device. */
+ int rc = VINF_SUCCESS;
+
+ void *pvIdCopy = NULL;
+ void *pvDataCopy = NULL;
+ if (cbData > 0)
+ {
+ pvDataCopy = RTMemDup(pvData, cbData);
+ if (!pvDataCopy)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pvIdCopy = RTMemDup(pszId, strlen(pszId) + 1);
+ if (!pvIdCopy)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ EmulatedUSB *pThis = (EmulatedUSB *)pv;
+ Console::SafeVMPtr ptrVM(pThis->m.pConsole);
+ if (ptrVM.isOk())
+ {
+ /* No wait. */
+ rc = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), 0 /* idDstCpu */,
+ (PFNRT)EmulatedUSB::eusbCallbackEMT, 5,
+ pThis, pvIdCopy, iEvent, pvDataCopy, cbData);
+ }
+ else
+ {
+ rc = VERR_INVALID_STATE;
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pvIdCopy);
+ RTMemFree(pvDataCopy);
+ }
+
+ return rc;
+}
+
+HRESULT EmulatedUSB::webcamPathFromId(com::Utf8Str *pPath, const char *pszId)
+{
+ HRESULT hr = S_OK;
+
+ Console::SafeVMPtr ptrVM(m.pConsole);
+ if (ptrVM.isOk())
+ {
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ WebcamsMap::const_iterator it;
+ for (it = m.webcams.begin(); it != m.webcams.end(); ++it)
+ {
+ EUSBWEBCAM *p = it->second;
+ if (p->HasId(pszId))
+ {
+ *pPath = it->first;
+ break;
+ }
+ }
+
+ if (it == m.webcams.end())
+ {
+ hr = E_FAIL;
+ }
+ alock.release();
+ }
+ else
+ {
+ hr = VBOX_E_INVALID_VM_STATE;
+ }
+
+ return hr;
+}
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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 */
}
diff --git a/src/VBox/Main/src-client/GuestCtrlPrivate.cpp b/src/VBox/Main/src-client/GuestCtrlPrivate.cpp
index 1a504aa4..1b0fb663 100644
--- a/src/VBox/Main/src-client/GuestCtrlPrivate.cpp
+++ b/src/VBox/Main/src-client/GuestCtrlPrivate.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2011-2012 Oracle Corporation
+ * Copyright (C) 2011-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -20,8 +20,11 @@
* Header Files *
******************************************************************************/
#include "GuestCtrlImplPrivate.h"
+#include "GuestSessionImpl.h"
+#include "VMMDev.h"
#include <iprt/asm.h>
+#include <iprt/cpp/utils.h> /* For unconst(). */
#include <iprt/ctype.h>
#ifdef DEBUG
# include <iprt/file.h>
@@ -37,306 +40,6 @@
* Structures and Typedefs *
******************************************************************************/
-GuestCtrlEvent::GuestCtrlEvent(void)
- : fCanceled(false),
- fCompleted(false),
- hEventSem(NIL_RTSEMEVENT),
- mRC(VINF_SUCCESS)
-{
-}
-
-GuestCtrlEvent::~GuestCtrlEvent(void)
-{
- Destroy();
-}
-
-int GuestCtrlEvent::Cancel(void)
-{
- int rc = VINF_SUCCESS;
- if (!ASMAtomicReadBool(&fCompleted))
- {
- if (!ASMAtomicReadBool(&fCanceled))
- {
- ASMAtomicXchgBool(&fCanceled, true);
-
- LogFlowThisFunc(("Cancelling event ...\n"));
- rc = hEventSem != NIL_RTSEMEVENT
- ? RTSemEventSignal(hEventSem) : VINF_SUCCESS;
- }
- }
-
- return rc;
-}
-
-bool GuestCtrlEvent::Canceled(void)
-{
- return ASMAtomicReadBool(&fCanceled);
-}
-
-void GuestCtrlEvent::Destroy(void)
-{
- int rc = Cancel();
- AssertRC(rc);
-
- if (hEventSem != NIL_RTSEMEVENT)
- {
- RTSemEventDestroy(hEventSem);
- hEventSem = NIL_RTSEMEVENT;
- }
-}
-
-int GuestCtrlEvent::Init(void)
-{
- return RTSemEventCreate(&hEventSem);
-}
-
-int GuestCtrlEvent::Signal(int rc /*= VINF_SUCCESS*/)
-{
- AssertReturn(hEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
-
- mRC = rc;
-
- return RTSemEventSignal(hEventSem);
-}
-
-int GuestCtrlEvent::Wait(ULONG uTimeoutMS)
-{
- LogFlowThisFuncEnter();
-
- AssertReturn(hEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
-
- RTMSINTERVAL msInterval = uTimeoutMS;
- if (!uTimeoutMS)
- msInterval = RT_INDEFINITE_WAIT;
- int rc = RTSemEventWait(hEventSem, msInterval);
- if (RT_SUCCESS(rc))
- ASMAtomicWriteBool(&fCompleted, true);
-
- LogFlowFuncLeaveRC(rc);
- return rc;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-GuestCtrlCallback::GuestCtrlCallback(void)
- : pvData(NULL),
- cbData(0),
- mType(VBOXGUESTCTRLCALLBACKTYPE_UNKNOWN),
- uFlags(0),
- pvPayload(NULL),
- cbPayload(0)
-{
-}
-
-GuestCtrlCallback::GuestCtrlCallback(eVBoxGuestCtrlCallbackType enmType)
- : pvData(NULL),
- cbData(0),
- mType(VBOXGUESTCTRLCALLBACKTYPE_UNKNOWN),
- uFlags(0),
- pvPayload(NULL),
- cbPayload(0)
-{
- int rc = Init(enmType);
- AssertRC(rc);
-}
-
-GuestCtrlCallback::~GuestCtrlCallback(void)
-{
- Destroy();
-}
-
-int GuestCtrlCallback::Init(eVBoxGuestCtrlCallbackType enmType)
-{
- AssertReturn(enmType > VBOXGUESTCTRLCALLBACKTYPE_UNKNOWN, VERR_INVALID_PARAMETER);
- Assert((pvData == NULL) && !cbData);
-
- switch (enmType)
- {
- case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
- {
- pvData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
- AssertPtrReturn(pvData, VERR_NO_MEMORY);
- RT_BZERO(pvData, sizeof(CALLBACKDATAEXECSTATUS));
- cbData = sizeof(CALLBACKDATAEXECSTATUS);
- break;
- }
-
- case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
- {
- pvData = (PCALLBACKDATAEXECOUT)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));
- AssertPtrReturn(pvData, VERR_NO_MEMORY);
- RT_BZERO(pvData, sizeof(CALLBACKDATAEXECOUT));
- cbData = sizeof(CALLBACKDATAEXECOUT);
- break;
- }
-
- case VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS:
- {
- pvData = (PCALLBACKDATAEXECINSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECINSTATUS));
- AssertPtrReturn(pvData, VERR_NO_MEMORY);
- RT_BZERO(pvData, sizeof(CALLBACKDATAEXECINSTATUS));
- cbData = sizeof(CALLBACKDATAEXECINSTATUS);
- break;
- }
-
- default:
- AssertMsgFailed(("Unknown callback type specified (%d)\n", enmType));
- break;
- }
-
- int rc = GuestCtrlEvent::Init();
- if (RT_SUCCESS(rc))
- mType = enmType;
-
- return rc;
-}
-
-void GuestCtrlCallback::Destroy(void)
-{
- GuestCtrlEvent::Destroy();
-
- switch (mType)
- {
- case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
- {
- PCALLBACKDATAEXECOUT pThis = (PCALLBACKDATAEXECOUT)pvData;
- AssertPtr(pThis);
- if (pThis->pvData)
- RTMemFree(pThis->pvData);
- }
-
- default:
- break;
- }
-
- mType = VBOXGUESTCTRLCALLBACKTYPE_UNKNOWN;
- if (pvData)
- {
- RTMemFree(pvData);
- pvData = NULL;
- }
- cbData = 0;
-
- if (pvPayload)
- {
- RTMemFree(pvPayload);
- pvPayload = NULL;
- }
- cbPayload = 0;
-}
-
-int GuestCtrlCallback::SetData(const void *pvCallback, size_t cbCallback)
-{
- if (!cbCallback)
- return VINF_SUCCESS;
- AssertPtr(pvCallback);
-
- switch (mType)
- {
- case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
- {
- PCALLBACKDATAEXECSTATUS pThis = (PCALLBACKDATAEXECSTATUS)pvData;
- PCALLBACKDATAEXECSTATUS pCB = (PCALLBACKDATAEXECSTATUS)pvCallback;
- Assert(cbCallback == sizeof(CALLBACKDATAEXECSTATUS));
-
- pThis->u32Flags = pCB->u32Flags;
- pThis->u32PID = pCB->u32PID;
- pThis->u32Status = pCB->u32Status;
- break;
- }
-
- case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
- {
- PCALLBACKDATAEXECOUT pThis = (PCALLBACKDATAEXECOUT)pvData;
- PCALLBACKDATAEXECOUT pCB = (PCALLBACKDATAEXECOUT)pvCallback;
- Assert(cbCallback == sizeof(CALLBACKDATAEXECOUT));
-
- pThis->cbData = pCB->cbData;
- if (pThis->cbData)
- {
- pThis->pvData = RTMemAlloc(pCB->cbData);
- AssertPtrReturn(pThis->pvData, VERR_NO_MEMORY);
- memcpy(pThis->pvData, pCB->pvData, pCB->cbData);
- }
- pThis->u32Flags = pCB->u32Flags;
- pThis->u32PID = pCB->u32PID;
- break;
- }
-
- case VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS:
- {
- PCALLBACKDATAEXECINSTATUS pThis = (PCALLBACKDATAEXECINSTATUS)pvData;
- PCALLBACKDATAEXECINSTATUS pCB = (PCALLBACKDATAEXECINSTATUS)pvCallback;
- Assert(cbCallback == sizeof(CALLBACKDATAEXECINSTATUS));
-
- pThis->cbProcessed = pCB->cbProcessed;
- pThis->u32Flags = pCB->u32Flags;
- pThis->u32PID = pCB->u32PID;
- pThis->u32Status = pCB->u32Status;
- break;
- }
-
- default:
- AssertMsgFailed(("Callback type not handled (%d)\n", mType));
- break;
- }
-
- return VINF_SUCCESS;
-}
-
-int GuestCtrlCallback::SetPayload(const void *pvToWrite, size_t cbToWrite)
-{
- if (!cbToWrite)
- return VINF_SUCCESS;
- AssertPtr(pvToWrite);
-
- Assert(pvPayload == NULL); /* Can't reuse callbacks! */
- pvPayload = RTMemAlloc(cbToWrite);
- if (!pvPayload)
- return VERR_NO_MEMORY;
-
- memcpy(pvPayload, pvToWrite, cbToWrite);
- cbPayload = cbToWrite;
-
- return VINF_SUCCESS;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-GuestProcessWaitEvent::GuestProcessWaitEvent(void)
- : mFlags(0),
- mResult(ProcessWaitResult_None)
-{
-}
-
-GuestProcessWaitEvent::GuestProcessWaitEvent(uint32_t uWaitFlags)
- : mFlags(uWaitFlags)
-{
- int rc = GuestCtrlEvent::Init();
- AssertRC(rc);
-}
-
-GuestProcessWaitEvent::~GuestProcessWaitEvent(void)
-{
- Destroy();
-}
-
-void GuestProcessWaitEvent::Destroy(void)
-{
- GuestCtrlEvent::Destroy();
-
- mFlags = ProcessWaitForFlag_None;
-}
-
-int GuestProcessWaitEvent::Signal(ProcessWaitResult_T enmResult, int rc /*= VINF_SUCCESS*/)
-{
- mResult = enmResult;
-
- return GuestCtrlEvent::Signal(rc);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
int GuestEnvironment::BuildEnvironmentBlock(void **ppvEnv, size_t *pcbEnv, uint32_t *pcEnvVars)
{
AssertPtrReturn(ppvEnv, VERR_INVALID_POINTER);
@@ -473,7 +176,7 @@ int GuestEnvironment::Set(const Utf8Str &strPair)
int rc = VINF_SUCCESS;
size_t p = 0;
- while(p < listPair.size() && RT_SUCCESS(rc))
+ while (p < listPair.size() && RT_SUCCESS(rc))
{
Utf8Str strKey = listPair.at(p++);
if ( strKey.isEmpty()
@@ -623,6 +326,32 @@ int GuestFsObjData::FromLs(const GuestProcessStreamBlock &strmBlk)
return rc;
}
+int GuestFsObjData::FromMkTemp(const GuestProcessStreamBlock &strmBlk)
+{
+ LogFlowFunc(("\n"));
+
+ int rc;
+
+ try
+ {
+#ifdef DEBUG
+ strmBlk.DumpToLog();
+#endif
+ /* Object name. */
+ mName = strmBlk.GetString("name");
+ if (mName.isEmpty()) throw VERR_NOT_FOUND;
+ /* Assign the stream block's rc. */
+ rc = strmBlk.GetRc();
+ }
+ catch (int rc2)
+ {
+ rc = rc2;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
int GuestFsObjData::FromStat(const GuestProcessStreamBlock &strmBlk)
{
LogFlowFunc(("\n"));
@@ -676,10 +405,10 @@ GuestProcessStreamBlock::GuestProcessStreamBlock(void)
/*
GuestProcessStreamBlock::GuestProcessStreamBlock(const GuestProcessStreamBlock &otherBlock)
{
- for (GuestCtrlStreamPairsIter it = otherBlock.m_mapPairs.begin();
+ for (GuestCtrlStreamPairsIter it = otherBlock.mPairs.begin();
it != otherBlock.end(); it++)
{
- m_mapPairs[it->first] = new
+ mPairs[it->first] = new
if (it->second.pszValue)
{
RTMemFree(it->second.pszValue);
@@ -700,17 +429,17 @@ GuestProcessStreamBlock::~GuestProcessStreamBlock()
*/
void GuestProcessStreamBlock::Clear(void)
{
- m_mapPairs.clear();
+ mPairs.clear();
}
#ifdef DEBUG
void GuestProcessStreamBlock::DumpToLog(void) const
{
LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n",
- this, m_mapPairs.size()));
+ this, mPairs.size()));
- for (GuestCtrlStreamPairMapIterConst it = m_mapPairs.begin();
- it != m_mapPairs.end(); it++)
+ for (GuestCtrlStreamPairMapIterConst it = mPairs.begin();
+ it != mPairs.end(); it++)
{
LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str()));
}
@@ -758,7 +487,22 @@ int64_t GuestProcessStreamBlock::GetInt64(const char *pszKey) const
*/
size_t GuestProcessStreamBlock::GetCount(void) const
{
- return m_mapPairs.size();
+ return mPairs.size();
+}
+
+/**
+ * Gets the return code (name = "rc") of this stream block.
+ *
+ * @return IPRT status code.
+ */
+int GuestProcessStreamBlock::GetRc(void) const
+{
+ const char *pszValue = GetString("rc");
+ if (pszValue)
+ {
+ return RTStrToInt16(pszValue);
+ }
+ return VERR_NOT_FOUND;
}
/**
@@ -773,8 +517,8 @@ const char* GuestProcessStreamBlock::GetString(const char *pszKey) const
try
{
- GuestCtrlStreamPairMapIterConst itPairs = m_mapPairs.find(Utf8Str(pszKey));
- if (itPairs != m_mapPairs.end())
+ GuestCtrlStreamPairMapIterConst itPairs = mPairs.find(Utf8Str(pszKey));
+ if (itPairs != mPairs.end())
return itPairs->second.mValue.c_str();
}
catch (const std::exception &ex)
@@ -836,17 +580,17 @@ int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue)
/* Take a shortcut and prevent crashes on some funny versions
* of STL if map is empty initially. */
- if (!m_mapPairs.empty())
+ if (!mPairs.empty())
{
- GuestCtrlStreamPairMapIter it = m_mapPairs.find(Utf8Key);
- if (it != m_mapPairs.end())
- m_mapPairs.erase(it);
+ GuestCtrlStreamPairMapIter it = mPairs.find(Utf8Key);
+ if (it != mPairs.end())
+ mPairs.erase(it);
}
if (pszValue)
{
GuestProcessStreamValue val(pszValue);
- m_mapPairs[Utf8Key] = val;
+ mPairs[Utf8Key] = val;
}
}
catch (const std::exception &ex)
@@ -976,22 +720,6 @@ void GuestProcessStream::Dump(const char *pszFile)
#endif
/**
- * Returns the current offset of the parser within
- * the internal data buffer.
- *
- * @return uint32_t Parser offset.
- */
-uint32_t GuestProcessStream::GetOffset()
-{
- return m_cbOffset;
-}
-
-uint32_t GuestProcessStream::GetSize()
-{
- return m_cbSize;
-}
-
-/**
* Tries to parse the next upcoming pair block within the internal
* buffer.
*
@@ -1075,3 +803,590 @@ int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock)
return rc;
}
+GuestBase::GuestBase(void)
+ : mConsole(NULL),
+ mNextContextID(0)
+{
+}
+
+GuestBase::~GuestBase(void)
+{
+}
+
+int GuestBase::baseInit(void)
+{
+ int rc = RTCritSectInit(&mWaitEventCritSect);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+void GuestBase::baseUninit(void)
+{
+ LogFlowThisFuncEnter();
+
+ int rc = RTCritSectDelete(&mWaitEventCritSect);
+
+ LogFlowFuncLeaveRC(rc);
+ /* No return value. */
+}
+
+int GuestBase::cancelWaitEvents(void)
+{
+ LogFlowThisFuncEnter();
+
+ int rc = RTCritSectEnter(&mWaitEventCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ GuestEventGroup::iterator itEventGroups = mWaitEventGroups.begin();
+ while (itEventGroups != mWaitEventGroups.end())
+ {
+ GuestWaitEvents::iterator itEvents = itEventGroups->second.begin();
+ while (itEvents != itEventGroups->second.end())
+ {
+ GuestWaitEvent *pEvent = itEvents->second;
+ AssertPtr(pEvent);
+
+ /*
+ * Just cancel the event, but don't remove it from the
+ * wait events map. Don't delete it though, this (hopefully)
+ * is done by the caller using unregisterWaitEvent().
+ */
+ int rc2 = pEvent->Cancel();
+ AssertRC(rc2);
+
+ itEvents++;
+ }
+
+ itEventGroups++;
+ }
+
+ int rc2 = RTCritSectLeave(&mWaitEventCritSect);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int GuestBase::dispatchGeneric(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
+{
+ LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
+
+ AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
+
+ int vrc = VINF_SUCCESS;
+
+ try
+ {
+ LogFlowFunc(("uFunc=%RU32, cParms=%RU32\n",
+ pCtxCb->uFunction, pSvcCb->mParms));
+
+ switch (pCtxCb->uFunction)
+ {
+ case GUEST_MSG_PROGRESS_UPDATE:
+ break;
+
+ case GUEST_MSG_REPLY:
+ {
+ if (pSvcCb->mParms >= 3)
+ {
+ int idx = 1; /* Current parameter index. */
+ CALLBACKDATA_MSG_REPLY dataCb;
+ /* pSvcCb->mpaParms[0] always contains the context ID. */
+ vrc = pSvcCb->mpaParms[idx++].getUInt32(&dataCb.uType);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCb->mpaParms[idx++].getUInt32(&dataCb.rc);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCb->mpaParms[idx++].getPointer(&dataCb.pvPayload, &dataCb.cbPayload);
+ AssertRCReturn(vrc, vrc);
+
+ GuestWaitEventPayload evPayload(dataCb.uType, dataCb.pvPayload, dataCb.cbPayload);
+ int rc2 = signalWaitEventInternal(pCtxCb, dataCb.rc, &evPayload);
+ AssertRC(rc2);
+ }
+ else
+ vrc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ default:
+ vrc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+ catch (int rc)
+ {
+ vrc = rc;
+ }
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
+{
+ AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
+
+ if ( uSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS
+ || uObjectID >= VBOX_GUESTCTRL_MAX_OBJECTS)
+ return VERR_INVALID_PARAMETER;
+
+ uint32_t uCount = ASMAtomicIncU32(&mNextContextID);
+ if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
+ uCount = 0;
+
+ uint32_t uNewContextID =
+ VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
+
+ *puContextID = uNewContextID;
+
+#if 0
+ LogFlowThisFunc(("mNextContextID=%RU32, uSessionID=%RU32, uObjectID=%RU32, uCount=%RU32, uNewContextID=%RU32\n",
+ mNextContextID, uSessionID, uObjectID, uCount, uNewContextID));
+#endif
+ return VINF_SUCCESS;
+}
+
+int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID,
+ GuestWaitEvent **ppEvent)
+{
+ GuestEventTypes eventTypesEmpty;
+ return registerWaitEvent(uSessionID, uObjectID, eventTypesEmpty, ppEvent);
+}
+
+int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID,
+ const GuestEventTypes &lstEvents,
+ GuestWaitEvent **ppEvent)
+{
+ AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
+
+ uint32_t uContextID;
+ int rc = generateContextID(uSessionID, uObjectID, &uContextID);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = RTCritSectEnter(&mWaitEventCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ try
+ {
+ GuestWaitEvent *pEvent = new GuestWaitEvent(uContextID, lstEvents);
+ AssertPtr(pEvent);
+
+ LogFlowThisFunc(("New event=%p, CID=%RU32\n", pEvent, uContextID));
+
+ /* Insert event into matching event group. This is for faster per-group
+ * lookup of all events later. */
+ for (GuestEventTypes::const_iterator itEvents = lstEvents.begin();
+ itEvents != lstEvents.end(); itEvents++)
+ {
+ mWaitEventGroups[(*itEvents)].insert(
+ std::pair<uint32_t, GuestWaitEvent*>(uContextID, pEvent));
+ /** @todo Check for key collision. */
+ }
+
+ /* Register event in regular event list. */
+ /** @todo Check for key collisions. */
+ mWaitEvents[uContextID] = pEvent;
+
+ *ppEvent = pEvent;
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ int rc2 = RTCritSectLeave(&mWaitEventCritSect);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ return rc;
+}
+
+int GuestBase::signalWaitEvent(VBoxEventType_T aType, IEvent *aEvent)
+{
+ int rc = RTCritSectEnter(&mWaitEventCritSect);
+#ifdef DEBUG
+ uint32_t cEvents = 0;
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ GuestEventGroup::iterator itGroup = mWaitEventGroups.find(aType);
+ if (itGroup != mWaitEventGroups.end())
+ {
+ GuestWaitEvents::iterator itEvents = itGroup->second.begin();
+ while (itEvents != itGroup->second.end())
+ {
+#ifdef DEBUG
+ LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %RU32: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
+ itEvents->second, aType, itEvents->first,
+ VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(itEvents->first),
+ VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(itEvents->first),
+ VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(itEvents->first)));
+#endif
+ ComPtr<IEvent> pThisEvent = aEvent;
+ Assert(!pThisEvent.isNull());
+ int rc2 = itEvents->second->SignalExternal(aEvent);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ if (RT_SUCCESS(rc2))
+ {
+ /* Remove the event from all other event groups (except the
+ * original one!) because it was signalled. */
+ AssertPtr(itEvents->second);
+ const GuestEventTypes evTypes = itEvents->second->Types();
+ for (GuestEventTypes::const_iterator itType = evTypes.begin();
+ itType != evTypes.end(); itType++)
+ {
+ if ((*itType) != aType) /* Only remove all other groups. */
+ {
+ /* Get current event group. */
+ GuestEventGroup::iterator evGroup = mWaitEventGroups.find((*itType));
+ Assert(evGroup != mWaitEventGroups.end());
+
+ /* Lookup event in event group. */
+ GuestWaitEvents::iterator evEvent = evGroup->second.find(itEvents->first /* Context ID */);
+ Assert(evEvent != evGroup->second.end());
+
+ LogFlowThisFunc(("Removing event=%p (type %ld)\n", evEvent->second, (*itType)));
+ evGroup->second.erase(evEvent);
+
+ LogFlowThisFunc(("%zu events for type=%ld left\n",
+ evGroup->second.size(), aType));
+ }
+ }
+
+ /* Remove the event from the passed-in event group. */
+ itGroup->second.erase(itEvents++);
+ }
+ else
+ itEvents++;
+#ifdef DEBUG
+ cEvents++;
+#endif
+ }
+ }
+
+ int rc2 = RTCritSectLeave(&mWaitEventCritSect);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+#ifdef DEBUG
+ LogFlowThisFunc(("Signalled %RU32 events, rc=%Rrc\n", cEvents, rc));
+#endif
+ return rc;
+}
+
+int GuestBase::signalWaitEventInternal(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
+ int guestRc, const GuestWaitEventPayload *pPayload)
+{
+ if (RT_SUCCESS(guestRc))
+ return signalWaitEventInternalEx(pCbCtx, VINF_SUCCESS,
+ 0 /* Guest rc */, pPayload);
+
+ return signalWaitEventInternalEx(pCbCtx, VERR_GSTCTL_GUEST_ERROR,
+ guestRc, pPayload);
+}
+
+int GuestBase::signalWaitEventInternalEx(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
+ int rc, int guestRc,
+ const GuestWaitEventPayload *pPayload)
+{
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ /* pPayload is optional. */
+
+ int rc2;
+ GuestWaitEvents::iterator itEvent = mWaitEvents.find(pCbCtx->uContextID);
+ if (itEvent != mWaitEvents.end())
+ {
+ LogFlowThisFunc(("Signalling event=%p (CID %RU32, rc=%Rrc, guestRc=%Rrc, pPayload=%p) ...\n",
+ itEvent->second, itEvent->first, rc, guestRc, pPayload));
+ GuestWaitEvent *pEvent = itEvent->second;
+ AssertPtr(pEvent);
+ rc2 = pEvent->SignalInternal(rc, guestRc, pPayload);
+ }
+ else
+ rc2 = VERR_NOT_FOUND;
+
+ return rc2;
+}
+
+void GuestBase::unregisterWaitEvent(GuestWaitEvent *pEvent)
+{
+ if (!pEvent) /* Nothing to unregister. */
+ return;
+
+ int rc = RTCritSectEnter(&mWaitEventCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowThisFunc(("pEvent=%p\n", pEvent));
+
+ const GuestEventTypes lstTypes = pEvent->Types();
+ for (GuestEventTypes::const_iterator itEvents = lstTypes.begin();
+ itEvents != lstTypes.end(); itEvents++)
+ {
+ /** @todo Slow O(n) lookup. Optimize this. */
+ GuestWaitEvents::iterator itCurEvent = mWaitEventGroups[(*itEvents)].begin();
+ while (itCurEvent != mWaitEventGroups[(*itEvents)].end())
+ {
+ if (itCurEvent->second == pEvent)
+ {
+ mWaitEventGroups[(*itEvents)].erase(itCurEvent++);
+ break;
+ }
+ else
+ itCurEvent++;
+ }
+ }
+
+ delete pEvent;
+ pEvent = NULL;
+
+ int rc2 = RTCritSectLeave(&mWaitEventCritSect);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+}
+
+/**
+ * Waits for a formerly registered guest event.
+ *
+ * @return IPRT status code.
+ * @param pEvent Pointer to event to wait for.
+ * @param uTimeoutMS Timeout (in ms) for waiting.
+ * @param pType Event type of following IEvent.
+ * Optional.
+ * @param ppEvent Pointer to IEvent which got triggered
+ * for this event. Optional.
+ */
+int GuestBase::waitForEvent(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
+ VBoxEventType_T *pType, IEvent **ppEvent)
+{
+ AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+ /* pType is optional. */
+ /* ppEvent is optional. */
+
+ int vrc = pEvent->Wait(uTimeoutMS);
+ if (RT_SUCCESS(vrc))
+ {
+ const ComPtr<IEvent> pThisEvent = pEvent->Event();
+ if (!pThisEvent.isNull()) /* Having a VBoxEventType_ event is optional. */
+ {
+ if (pType)
+ {
+ HRESULT hr = pThisEvent->COMGETTER(Type)(pType);
+ if (FAILED(hr))
+ vrc = VERR_COM_UNEXPECTED;
+ }
+ if ( RT_SUCCESS(vrc)
+ && ppEvent)
+ pThisEvent.queryInterfaceTo(ppEvent);
+
+ unconst(pThisEvent).setNull();
+ }
+ }
+
+ return vrc;
+}
+
+GuestObject::GuestObject(void)
+ : mSession(NULL),
+ mObjectID(0)
+{
+}
+
+GuestObject::~GuestObject(void)
+{
+}
+
+int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
+{
+ AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
+
+ mConsole = pConsole;
+ mSession = pSession;
+ mObjectID = uObjectID;
+
+ return VINF_SUCCESS;
+}
+
+int GuestObject::registerWaitEvent(const GuestEventTypes &lstEvents,
+ GuestWaitEvent **ppEvent)
+{
+ AssertPtr(mSession);
+ return GuestBase::registerWaitEvent(mSession->getId(), mObjectID, lstEvents, ppEvent);
+}
+
+int GuestObject::sendCommand(uint32_t uFunction,
+ uint32_t uParms, PVBOXHGCMSVCPARM paParms)
+{
+#ifndef VBOX_GUESTCTRL_TEST_CASE
+ ComObjPtr<Console> pConsole = mConsole;
+ Assert(!pConsole.isNull());
+
+ int vrc = VERR_HGCM_SERVICE_NOT_FOUND;
+
+ /* Forward the information to the VMM device. */
+ VMMDev *pVMMDev = pConsole->getVMMDev();
+ if (pVMMDev)
+ {
+ LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
+ vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
+ if (RT_FAILURE(vrc))
+ {
+ /** @todo What to do here? */
+ }
+ }
+#else
+ LogFlowThisFuncEnter();
+
+ /* Not needed within testcases. */
+ int vrc = VINF_SUCCESS;
+#endif
+ return vrc;
+}
+
+GuestWaitEventBase::GuestWaitEventBase(void)
+ : mfAborted(false),
+ mCID(0),
+ mEventSem(NIL_RTSEMEVENT),
+ mRc(VINF_SUCCESS),
+ mGuestRc(VINF_SUCCESS)
+{
+}
+
+GuestWaitEventBase::~GuestWaitEventBase(void)
+{
+}
+
+int GuestWaitEventBase::Init(uint32_t uCID)
+{
+ mCID = uCID;
+
+ return RTSemEventCreate(&mEventSem);
+}
+
+int GuestWaitEventBase::SignalInternal(int rc, int guestRc,
+ const GuestWaitEventPayload *pPayload)
+{
+ if (ASMAtomicReadBool(&mfAborted))
+ return VERR_CANCELLED;
+
+#ifdef VBOX_STRICT
+ if (rc == VERR_GSTCTL_GUEST_ERROR)
+ AssertMsg(RT_FAILURE(guestRc), ("Guest error indicated but no actual guest error set (%Rrc)\n", guestRc));
+ else
+ AssertMsg(RT_SUCCESS(guestRc), ("No guest error indicated but actual guest error set (%Rrc)\n", guestRc));
+#endif
+
+ int rc2;
+ if (pPayload)
+ rc2 = mPayload.CopyFromDeep(*pPayload);
+ else
+ rc2 = VINF_SUCCESS;
+ if (RT_SUCCESS(rc2))
+ {
+ mRc = rc;
+ mGuestRc = guestRc;
+
+ rc2 = RTSemEventSignal(mEventSem);
+ }
+
+ return rc2;
+}
+
+int GuestWaitEventBase::Wait(RTMSINTERVAL uTimeoutMS)
+{
+ int rc = VINF_SUCCESS;
+
+ if (ASMAtomicReadBool(&mfAborted))
+ rc = VERR_CANCELLED;
+
+ if (RT_SUCCESS(rc))
+ {
+ AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
+
+ RTMSINTERVAL msInterval = uTimeoutMS;
+ if (!uTimeoutMS)
+ msInterval = RT_INDEFINITE_WAIT;
+ rc = RTSemEventWait(mEventSem, msInterval);
+ if (ASMAtomicReadBool(&mfAborted))
+ rc = VERR_CANCELLED;
+ if (RT_SUCCESS(rc))
+ {
+ /* If waiting succeeded, return the overall
+ * result code. */
+ rc = mRc;
+ }
+ }
+
+ return rc;
+}
+
+GuestWaitEvent::GuestWaitEvent(uint32_t uCID,
+ const GuestEventTypes &lstEvents)
+{
+ int rc2 = Init(uCID);
+ AssertRC(rc2); /** @todo Throw exception here. */
+
+ mEventTypes = lstEvents;
+}
+
+GuestWaitEvent::GuestWaitEvent(uint32_t uCID)
+{
+ int rc2 = Init(uCID);
+ AssertRC(rc2); /** @todo Throw exception here. */
+}
+
+GuestWaitEvent::~GuestWaitEvent(void)
+{
+
+}
+
+/**
+ * Cancels the event.
+ */
+int GuestWaitEvent::Cancel(void)
+{
+ AssertReturn(!mfAborted, VERR_CANCELLED);
+ ASMAtomicWriteBool(&mfAborted, true);
+
+#ifdef DEBUG_andy
+ LogFlowThisFunc(("Cancelling %p ...\n"));
+#endif
+ return RTSemEventSignal(mEventSem);
+}
+
+int GuestWaitEvent::Init(uint32_t uCID)
+{
+ return GuestWaitEventBase::Init(uCID);
+}
+
+/**
+ * Signals the event.
+ *
+ * @return IPRT status code.
+ * @param pEvent Public IEvent to associate.
+ * Optional.
+ */
+int GuestWaitEvent::SignalExternal(IEvent *pEvent)
+{
+ AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
+
+ if (pEvent)
+ mEvent = pEvent;
+
+ return RTSemEventSignal(mEventSem);
+}
+
diff --git a/src/VBox/Main/src-client/GuestDirectoryImpl.cpp b/src/VBox/Main/src-client/GuestDirectoryImpl.cpp
index 36d262b9..52d0cf95 100644
--- a/src/VBox/Main/src-client/GuestDirectoryImpl.cpp
+++ b/src/VBox/Main/src-client/GuestDirectoryImpl.cpp
@@ -1,11 +1,11 @@
/* $Id: GuestDirectoryImpl.cpp $ */
/** @file
- * VirtualBox Main - XXX.
+ * VirtualBox Main - Guest directory 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;
@@ -58,54 +58,72 @@ void GuestDirectory::FinalRelease(void)
// public initializer/uninitializer for internal purposes only
/////////////////////////////////////////////////////////////////////////////
-int GuestDirectory::init(GuestSession *aSession,
- const Utf8Str &strPath, const Utf8Str &strFilter, uint32_t uFlags)
+int GuestDirectory::init(Console *pConsole, GuestSession *pSession,
+ ULONG uDirID, const GuestDirectoryOpenInfo &openInfo)
{
- LogFlowThisFunc(("strPath=%s, strFilter=%s, uFlags=%x\n",
- strPath.c_str(), strFilter.c_str(), uFlags));
+ LogFlowThisFunc(("pConsole=%p, pSession=%p, uDirID=%RU32, strPath=%s, strFilter=%s, uFlags=%x\n",
+ pConsole, pSession, uDirID, openInfo.mPath.c_str(), openInfo.mFilter.c_str(),
+ openInfo.mFlags));
+
+ AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
/* Enclose the state transition NotReady->InInit->Ready. */
AutoInitSpan autoInitSpan(this);
AssertReturn(autoInitSpan.isOk(), E_FAIL);
- mData.mSession = aSession;
- mData.mName = strPath;
- mData.mFilter = strFilter;
- mData.mFlags = uFlags;
-
- /* Start the directory process on the guest. */
- GuestProcessStartupInfo procInfo;
- procInfo.mName = Utf8StrFmt(tr("Reading directory \"%s\"", strPath.c_str()));
- procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_LS);
- procInfo.mTimeoutMS = 5 * 60 * 1000; /* 5 minutes timeout. */
- procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
-
- procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
- /* We want the long output format which contains all the object details. */
- procInfo.mArguments.push_back(Utf8Str("-l"));
+#ifndef VBOX_WITH_GUEST_CONTROL
+ autoInitSpan.setSucceeded();
+ return VINF_SUCCESS;
+#else
+ int vrc = bindToSession(pConsole, pSession, uDirID /* Object ID */);
+ if (RT_SUCCESS(vrc))
+ {
+ mSession = pSession;
+
+ mData.mID = uDirID;
+ mData.mOpenInfo = openInfo;
+ }
+
+ if (RT_SUCCESS(vrc))
+ {
+ /* Start the directory process on the guest. */
+ GuestProcessStartupInfo procInfo;
+ procInfo.mName = Utf8StrFmt(tr("Reading directory \"%s\"", openInfo.mPath.c_str()));
+ procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_LS);
+ procInfo.mTimeoutMS = 5 * 60 * 1000; /* 5 minutes timeout. */
+ procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
+
+ procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
+ /* We want the long output format which contains all the object details. */
+ procInfo.mArguments.push_back(Utf8Str("-l"));
#if 0 /* Flags are not supported yet. */
- if (uFlags & DirectoryOpenFlag_NoSymlinks)
- procInfo.mArguments.push_back(Utf8Str("--nosymlinks")); /** @todo What does GNU here? */
+ if (uFlags & DirectoryOpenFlag_NoSymlinks)
+ procInfo.mArguments.push_back(Utf8Str("--nosymlinks")); /** @todo What does GNU here? */
#endif
- /** @todo Recursion support? */
- procInfo.mArguments.push_back(strPath); /* The directory we want to open. */
+ /** @todo Recursion support? */
+ procInfo.mArguments.push_back(openInfo.mPath); /* The directory we want to open. */
+
+ /*
+ * Start the process asynchronously and keep it around so that we can use
+ * it later in subsequent read() calls.
+ * Note: No guest rc available because operation is asynchronous.
+ */
+ vrc = mData.mProcessTool.Init(mSession, procInfo,
+ true /* Async */, NULL /* Guest rc */);
+ }
- /*
- * Start the process asynchronously and keep it around so that we can use
- * it later in subsequent read() calls.
- * Note: No guest rc available because operation is asynchronous.
- */
- int rc = mData.mProcessTool.Init(mData.mSession, procInfo,
- true /* Async */, NULL /* Guest rc */);
- if (RT_SUCCESS(rc))
+ if (RT_SUCCESS(vrc))
{
/* Confirm a successful initialization when it's the case. */
autoInitSpan.setSucceeded();
- return rc;
+ return vrc;
}
+ else
+ autoInitSpan.setFailed();
- autoInitSpan.setFailed();
- return rc;
+ return vrc;
+#endif /* VBOX_WITH_GUEST_CONTROL */
}
/**
@@ -114,7 +132,7 @@ int GuestDirectory::init(GuestSession *aSession,
*/
void GuestDirectory::uninit(void)
{
- LogFlowThisFunc(("\n"));
+ LogFlowThisFuncEnter();
/* Enclose the state transition Ready->InUninit->NotReady. */
AutoUninitSpan autoUninitSpan(this);
@@ -138,7 +156,7 @@ STDMETHODIMP GuestDirectory::COMGETTER(DirectoryName)(BSTR *aName)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- mData.mName.cloneTo(aName);
+ mData.mOpenInfo.mPath.cloneTo(aName);
return S_OK;
}
@@ -154,7 +172,7 @@ STDMETHODIMP GuestDirectory::COMGETTER(Filter)(BSTR *aFilter)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- mData.mFilter.cloneTo(aFilter);
+ mData.mOpenInfo.mFilter.cloneTo(aFilter);
return S_OK;
}
@@ -162,6 +180,96 @@ STDMETHODIMP GuestDirectory::COMGETTER(Filter)(BSTR *aFilter)
// private methods
/////////////////////////////////////////////////////////////////////////////
+int GuestDirectory::callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
+{
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
+
+ LogFlowThisFunc(("strPath=%s, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
+ mData.mOpenInfo.mPath.c_str(), pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
+
+ int vrc;
+ switch (pCbCtx->uFunction)
+ {
+ case GUEST_DIR_NOTIFY:
+ {
+ int idx = 1; /* Current parameter index. */
+ CALLBACKDATA_DIR_NOTIFY dataCb;
+ /* pSvcCb->mpaParms[0] always contains the context ID. */
+ pSvcCb->mpaParms[idx++].getUInt32(&dataCb.uType);
+ pSvcCb->mpaParms[idx++].getUInt32(&dataCb.rc);
+
+ int guestRc = (int)dataCb.rc; /* uint32_t vs. int. */
+
+ LogFlowFunc(("uType=%RU32, guestRc=%Rrc\n",
+ dataCb.uType, guestRc));
+
+ switch (dataCb.uType)
+ {
+ /* Nothing here yet, nothing to dispatch further. */
+
+ default:
+ vrc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ break;
+ }
+
+ default:
+ /* Silently ignore not implemented functions. */
+ vrc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+#ifdef DEBUG
+ LogFlowFuncLeaveRC(vrc);
+#endif
+ return vrc;
+}
+
+/* static */
+Utf8Str GuestDirectory::guestErrorToString(int guestRc)
+{
+ Utf8Str strError;
+
+ /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
+ switch (guestRc)
+ {
+ case VERR_DIR_NOT_EMPTY:
+ strError += Utf8StrFmt("Directoy is not empty");
+ break;
+
+ default:
+ strError += Utf8StrFmt("%Rrc", guestRc);
+ break;
+ }
+
+ return strError;
+}
+
+/**
+ * Called by IGuestSession right before this directory gets
+ * removed from the public directory list.
+ */
+int GuestDirectory::onRemove(void)
+{
+ LogFlowThisFuncEnter();
+
+ int vrc = VINF_SUCCESS;
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+/* static */
+HRESULT GuestDirectory::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, GuestDirectory::guestErrorToString(guestRc).c_str());
+}
+
// implementation of public methods
/////////////////////////////////////////////////////////////////////////////
@@ -175,20 +283,38 @@ STDMETHODIMP GuestDirectory::Close(void)
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- AssertPtr(mData.mSession);
- int rc = mData.mSession->directoryRemoveFromList(this);
+ HRESULT hr = S_OK;
+
+ int guestRc;
+ int rc = mData.mProcessTool.Terminate(30 * 1000, &guestRc);
+ if (RT_FAILURE(rc))
+ {
+ switch (rc)
+ {
+ case VERR_GSTCTL_GUEST_ERROR:
+ hr = GuestProcess::setErrorExternal(this, guestRc);
+ break;
- mData.mProcessTool.Terminate();
+ case VERR_NOT_SUPPORTED:
+ /* Silently skip old Guest Additions which do not support killing the
+ * the guest directory handling process. */
+ break;
- /*
- * Release autocaller before calling uninit.
- */
- autoCaller.release();
+ default:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Terminating open guest directory \"%s\" failed: %Rrc"),
+ mData.mOpenInfo.mPath.c_str(), rc);
+ break;
+ }
+ }
- uninit();
+ AssertPtr(mSession);
+ int rc2 = mSession->directoryRemoveFromList(this);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
- LogFlowFuncLeaveRC(rc);
- return S_OK;
+ LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
+ return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -262,34 +388,34 @@ STDMETHODIMP GuestDirectory::Read(IFsObjInfo **aInfo)
{
switch (rc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
hr = GuestProcess::setErrorExternal(this, guestRc);
break;
case VERR_ACCESS_DENIED:
hr = setError(VBOX_E_IPRT_ERROR, tr("Reading directory \"%s\" failed: Unable to read / access denied"),
- mData.mName.c_str());
+ mData.mOpenInfo.mPath.c_str());
break;
case VERR_PATH_NOT_FOUND:
hr = setError(VBOX_E_IPRT_ERROR, tr("Reading directory \"%s\" failed: Path not found"),
- mData.mName.c_str());
+ mData.mOpenInfo.mPath.c_str());
break;
case VERR_NO_MORE_FILES:
/* See SDK reference. */
hr = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No more entries for directory \"%s\""),
- mData.mName.c_str());
+ mData.mOpenInfo.mPath.c_str());
break;
default:
hr = setError(VBOX_E_IPRT_ERROR, tr("Error while reading directory \"%s\": %Rrc\n"),
- mData.mName.c_str(), rc);
+ mData.mOpenInfo.mPath.c_str(), rc);
break;
}
}
- LogFlowFuncLeaveRC(rc);
+ LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
diff --git a/src/VBox/Main/src-client/GuestDnDImpl.cpp b/src/VBox/Main/src-client/GuestDnDImpl.cpp
index 2e07a950..6732e163 100644
--- a/src/VBox/Main/src-client/GuestDnDImpl.cpp
+++ b/src/VBox/Main/src-client/GuestDnDImpl.cpp
@@ -440,7 +440,7 @@ void GuestDnDPrivate::toFormatSafeArray(const RTCString &strFormats, ComSafeArra
{
RTCList<RTCString> list = strFormats.split("\r\n");
size_t i = 0;
- while(i < list.size())
+ while (i < list.size())
{
/* Only keep allowed format types. */
if (!m_sstrAllowedMimeTypes.contains(list.at(i)))
diff --git a/src/VBox/Main/src-client/GuestFileImpl.cpp b/src/VBox/Main/src-client/GuestFileImpl.cpp
index 19afbcc2..8a0ce53e 100644
--- a/src/VBox/Main/src-client/GuestFileImpl.cpp
+++ b/src/VBox/Main/src-client/GuestFileImpl.cpp
@@ -1,11 +1,10 @@
-
/* $Id: GuestFileImpl.cpp $ */
/** @file
- * VirtualBox Main - XXX.
+ * VirtualBox Main - Guest file 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,11 +22,18 @@
#include "GuestFileImpl.h"
#include "GuestSessionImpl.h"
#include "GuestCtrlImplPrivate.h"
+#include "ConsoleImpl.h"
+#include "VirtualBoxErrorInfoImpl.h"
#include "Global.h"
#include "AutoCaller.h"
+#include "VBoxEvents.h"
+
+#include <iprt/cpp/utils.h> /* For unconst(). */
+#include <iprt/file.h>
#include <VBox/com/array.h>
+#include <VBox/com/listeners.h>
#ifdef LOG_GROUP
#undef LOG_GROUP
@@ -36,6 +42,64 @@
#include <VBox/log.h>
+/**
+ * Internal listener class to serve events in an
+ * active manner, e.g. without polling delays.
+ */
+class GuestFileListener
+{
+public:
+
+ GuestFileListener(void)
+ {
+ }
+
+ HRESULT init(GuestFile *pFile)
+ {
+ AssertPtrReturn(pFile, E_POINTER);
+ mFile = pFile;
+ return S_OK;
+ }
+
+ void uninit(void)
+ {
+ mFile = NULL;
+ }
+
+ STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
+ {
+ switch (aType)
+ {
+ case VBoxEventType_OnGuestFileStateChanged:
+ case VBoxEventType_OnGuestFileOffsetChanged:
+ case VBoxEventType_OnGuestFileRead:
+ case VBoxEventType_OnGuestFileWrite:
+ {
+ AssertPtrReturn(mFile, E_POINTER);
+ int rc2 = mFile->signalWaitEvent(aType, aEvent);
+#ifdef DEBUG_andy
+ LogFlowFunc(("Signalling events of type=%RU32, file=%p resulted in rc=%Rrc\n",
+ aType, mFile, rc2));
+#endif
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("Unhandled event %RU32\n", aType));
+ break;
+ }
+
+ return S_OK;
+ }
+
+private:
+
+ GuestFile *mFile;
+};
+typedef ListenerImpl<GuestFileListener, GuestFile*> GuestFileListenerImpl;
+
+VBOX_LISTENER_DECLARE(GuestFileListenerImpl)
+
// constructor / destructor
/////////////////////////////////////////////////////////////////////////////
@@ -43,7 +107,7 @@ DEFINE_EMPTY_CTOR_DTOR(GuestFile)
HRESULT GuestFile::FinalConstruct(void)
{
- LogFlowThisFunc(("\n"));
+ LogFlowThisFuncEnter();
return BaseFinalConstruct();
}
@@ -58,29 +122,100 @@ void GuestFile::FinalRelease(void)
// public initializer/uninitializer for internal purposes only
/////////////////////////////////////////////////////////////////////////////
-int GuestFile::init(GuestSession *pSession, const Utf8Str &strPath,
- const Utf8Str &strOpenMode, const Utf8Str &strDisposition, uint32_t uCreationMode,
- int64_t iOffset, int *pGuestRc)
+/**
+ * Initializes a file object but does *not* open the file on the guest
+ * yet. This is done in the dedidcated openFile call.
+ *
+ * @return IPRT status code.
+ * @param pConsole Pointer to console object.
+ * @param pSession Pointer to session object.
+ * @param uFileID Host-based file ID (part of the context ID).
+ * @param openInfo File opening information.
+ */
+int GuestFile::init(Console *pConsole, GuestSession *pSession,
+ ULONG uFileID, const GuestFileOpenInfo &openInfo)
{
- /* Enclose the state transition NotReady->InInit->Ready. */
- AutoInitSpan autoInitSpan(this);
- AssertReturn(autoInitSpan.isOk(), E_FAIL);
+ LogFlowThisFunc(("pConsole=%p, pSession=%p, uFileID=%RU32, strPath=%s\n",
+ pConsole, pSession, uFileID, openInfo.mFileName.c_str()));
- mData.mSession = pSession;
- mData.mCreationMode = uCreationMode;
- mData.mDisposition = GuestFile::getDispositionFromString(strDisposition);
- mData.mFileName = strPath;
- mData.mInitialSize = 0;
- mData.mOpenMode = GuestFile::getOpenModeFromString(strOpenMode);
- mData.mOffset = iOffset;
+ AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSession, VERR_INVALID_POINTER);
- /** @todo Validate parameters! */
- /** @todo Implement guest side file handling! */
+ /* Enclose the state transition NotReady->InInit->Ready. */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
- /* Confirm a successful initialization when it's the case. */
+#ifndef VBOX_WITH_GUEST_CONTROL
autoInitSpan.setSucceeded();
-
return VINF_SUCCESS;
+#else
+ int vrc = bindToSession(pConsole, pSession, uFileID /* Object ID */);
+ if (RT_SUCCESS(vrc))
+ {
+ mSession = pSession;
+
+ mData.mID = uFileID;
+ mData.mInitialSize = 0;
+ mData.mStatus = FileStatus_Undefined;
+ mData.mOpenInfo = openInfo;
+
+ unconst(mEventSource).createObject();
+ HRESULT hr = mEventSource->init();
+ if (FAILED(hr))
+ vrc = VERR_COM_UNEXPECTED;
+ }
+
+ if (RT_SUCCESS(vrc))
+ {
+ try
+ {
+ GuestFileListener *pListener = new GuestFileListener();
+ ComObjPtr<GuestFileListenerImpl> thisListener;
+ HRESULT hr = thisListener.createObject();
+ if (SUCCEEDED(hr))
+ hr = thisListener->init(pListener, this);
+
+ if (SUCCEEDED(hr))
+ {
+ com::SafeArray <VBoxEventType_T> eventTypes;
+ eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
+ eventTypes.push_back(VBoxEventType_OnGuestFileOffsetChanged);
+ eventTypes.push_back(VBoxEventType_OnGuestFileRead);
+ eventTypes.push_back(VBoxEventType_OnGuestFileWrite);
+ hr = mEventSource->RegisterListener(thisListener,
+ ComSafeArrayAsInParam(eventTypes),
+ TRUE /* Active listener */);
+ if (SUCCEEDED(hr))
+ {
+ vrc = baseInit();
+ if (RT_SUCCESS(vrc))
+ {
+ mLocalListener = thisListener;
+ }
+ }
+ else
+ vrc = VERR_COM_UNEXPECTED;
+ }
+ else
+ vrc = VERR_COM_UNEXPECTED;
+ }
+ catch(std::bad_alloc &)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+ }
+
+ if (RT_SUCCESS(vrc))
+ {
+ /* Confirm a successful initialization when it's the case. */
+ autoInitSpan.setSucceeded();
+ }
+ else
+ autoInitSpan.setFailed();
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+#endif /* VBOX_WITH_GUEST_CONTROL */
}
/**
@@ -89,13 +224,16 @@ int GuestFile::init(GuestSession *pSession, const Utf8Str &strPath,
*/
void GuestFile::uninit(void)
{
- LogFlowThisFunc(("\n"));
-
/* Enclose the state transition Ready->InUninit->NotReady. */
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
return;
+ LogFlowThisFuncEnter();
+
+#ifdef VBOX_WITH_GUEST_CONTROL
+ baseUninit();
+#endif
LogFlowThisFuncLeave();
}
@@ -114,13 +252,13 @@ STDMETHODIMP GuestFile::COMGETTER(CreationMode)(ULONG *aCreationMode)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- *aCreationMode = mData.mCreationMode;
+ *aCreationMode = mData.mOpenInfo.mCreationMode;
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
-STDMETHODIMP GuestFile::COMGETTER(Disposition)(ULONG *aDisposition)
+STDMETHODIMP GuestFile::COMGETTER(Disposition)(BSTR *aDisposition)
{
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
@@ -132,7 +270,24 @@ STDMETHODIMP GuestFile::COMGETTER(Disposition)(ULONG *aDisposition)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- *aDisposition = mData.mDisposition;
+ mData.mOpenInfo.mDisposition.cloneTo(aDisposition);
+
+ return S_OK;
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
+STDMETHODIMP GuestFile::COMGETTER(EventSource)(IEventSource ** aEventSource)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+ ReturnComNotImplemented();
+#else
+ CheckComArgOutPointerValid(aEventSource);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ /* No need to lock - lifetime constant. */
+ mEventSource.queryInterfaceTo(aEventSource);
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
@@ -150,7 +305,25 @@ STDMETHODIMP GuestFile::COMGETTER(FileName)(BSTR *aFileName)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- mData.mFileName.cloneTo(aFileName);
+ mData.mOpenInfo.mFileName.cloneTo(aFileName);
+
+ return S_OK;
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
+STDMETHODIMP GuestFile::COMGETTER(Id)(ULONG *aID)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+ ReturnComNotImplemented();
+#else
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ CheckComArgOutPointerValid(aID);
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aID = mData.mID;
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
@@ -186,13 +359,13 @@ STDMETHODIMP GuestFile::COMGETTER(Offset)(LONG64 *aOffset)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- *aOffset = mData.mOffset;
+ *aOffset = mData.mOffCurrent;
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
-STDMETHODIMP GuestFile::COMGETTER(OpenMode)(ULONG *aOpenMode)
+STDMETHODIMP GuestFile::COMGETTER(OpenMode)(BSTR *aOpenMode)
{
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
@@ -204,7 +377,25 @@ STDMETHODIMP GuestFile::COMGETTER(OpenMode)(ULONG *aOpenMode)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- *aOpenMode = mData.mOpenMode;
+ mData.mOpenInfo.mOpenMode.cloneTo(aOpenMode);
+
+ return S_OK;
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
+STDMETHODIMP GuestFile::COMGETTER(Status)(FileStatus_T *aStatus)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+ ReturnComNotImplemented();
+#else
+ LogFlowThisFuncEnter();
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aStatus = mData.mStatus;
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
@@ -213,16 +404,865 @@ STDMETHODIMP GuestFile::COMGETTER(OpenMode)(ULONG *aOpenMode)
// private methods
/////////////////////////////////////////////////////////////////////////////
+int GuestFile::callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
+{
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
+
+ LogFlowThisFunc(("strName=%s, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
+ mData.mOpenInfo.mFileName.c_str(), pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
+
+ int vrc;
+ switch (pCbCtx->uFunction)
+ {
+ case GUEST_DISCONNECTED:
+ vrc = onGuestDisconnected(pCbCtx, pSvcCb);
+ break;
+
+ case GUEST_FILE_NOTIFY:
+ vrc = onFileNotify(pCbCtx, pSvcCb);
+ break;
+
+ default:
+ /* Silently ignore not implemented functions. */
+ vrc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+#ifdef DEBUG
+ LogFlowFuncLeaveRC(vrc);
+#endif
+ return vrc;
+}
+
+int GuestFile::closeFile(int *pGuestRc)
+{
+ LogFlowThisFunc(("strFile=%s\n", mData.mOpenInfo.mFileName.c_str()));
+
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
+ try
+ {
+ eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
+
+ vrc = registerWaitEvent(eventTypes, &pEvent);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /* Prepare HGCM call. */
+ VBOXHGCMSVCPARM paParms[4];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setUInt32(mData.mID /* Guest file ID */);
+
+ vrc = sendCommand(HOST_FILE_CLOSE, i, paParms);
+ if (RT_SUCCESS(vrc))
+ vrc = waitForStatusChange(pEvent, 30 * 1000 /* Timeout in ms */,
+ NULL /* FileStatus */, pGuestRc);
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
/* static */
-uint32_t GuestFile::getDispositionFromString(const Utf8Str &strDisposition)
+Utf8Str GuestFile::guestErrorToString(int guestRc)
{
- return 0; /** @todo Implement me! */
+ Utf8Str strError;
+
+ /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
+ switch (guestRc)
+ {
+ case VERR_ALREADY_EXISTS:
+ strError += Utf8StrFmt(tr("File already exists"));
+ break;
+
+ case VERR_FILE_NOT_FOUND:
+ strError += Utf8StrFmt(tr("File not found"));
+ break;
+
+ case VERR_NET_HOST_NOT_FOUND:
+ strError += Utf8StrFmt(tr("Host name not found"));
+ break;
+
+ case VERR_SHARING_VIOLATION:
+ strError += Utf8StrFmt(tr("Sharing violation"));
+ break;
+
+ default:
+ strError += Utf8StrFmt("%Rrc", guestRc);
+ break;
+ }
+
+ return strError;
+}
+
+int GuestFile::onFileNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
+{
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
+
+ LogFlowThisFuncEnter();
+
+ if (pSvcCbData->mParms < 3)
+ return VERR_INVALID_PARAMETER;
+
+ int vrc = VINF_SUCCESS;
+
+ int idx = 1; /* Current parameter index. */
+ CALLBACKDATA_FILE_NOTIFY dataCb;
+ /* pSvcCb->mpaParms[0] always contains the context ID. */
+ pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.uType);
+ pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.rc);
+
+ FileStatus_T fileStatus = FileStatus_Undefined;
+ int guestRc = (int)dataCb.rc; /* uint32_t vs. int. */
+
+ LogFlowFunc(("uType=%RU32, guestRc=%Rrc\n",
+ dataCb.uType, guestRc));
+
+ if (RT_FAILURE(guestRc))
+ {
+ int rc2 = setFileStatus(FileStatus_Error, guestRc);
+ AssertRC(rc2);
+
+ rc2 = signalWaitEventInternal(pCbCtx,
+ guestRc, NULL /* pPayload */);
+ AssertRC(rc2);
+
+ return VINF_SUCCESS; /* Report to the guest. */
+ }
+
+ switch (dataCb.uType)
+ {
+ case GUEST_FILE_NOTIFYTYPE_ERROR:
+ {
+ int rc2 = setFileStatus(FileStatus_Error, guestRc);
+ AssertRC(rc2);
+
+ break;
+ }
+
+ case GUEST_FILE_NOTIFYTYPE_OPEN:
+ {
+ if (pSvcCbData->mParms == 4)
+ {
+ pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.u.open.uHandle);
+
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertMsg(mData.mID == VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID),
+ ("File ID %RU32 does not match context ID %RU32\n", mData.mID,
+ VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID)));
+
+ /* Set the initial offset. On the guest the whole opening operation
+ * would fail if an initial seek isn't possible. */
+ mData.mOffCurrent = mData.mOpenInfo.mInitialOffset;
+ }
+
+ /* Set the process status. */
+ int rc2 = setFileStatus(FileStatus_Open, guestRc);
+ AssertRC(rc2);
+ }
+ else
+ vrc = VERR_NOT_SUPPORTED;
+
+ break;
+ }
+
+ case GUEST_FILE_NOTIFYTYPE_CLOSE:
+ {
+ int rc2 = setFileStatus(FileStatus_Closed, guestRc);
+ AssertRC(rc2);
+
+ break;
+ }
+
+ case GUEST_FILE_NOTIFYTYPE_READ:
+ {
+ if (pSvcCbData->mParms == 4)
+ {
+ pSvcCbData->mpaParms[idx++].getPointer(&dataCb.u.read.pvData,
+ &dataCb.u.read.cbData);
+ uint32_t cbRead = dataCb.u.read.cbData;
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ mData.mOffCurrent += cbRead;
+
+ alock.release();
+
+ com::SafeArray<BYTE> data((size_t)cbRead);
+ data.initFrom((BYTE*)dataCb.u.read.pvData, cbRead);
+
+ fireGuestFileReadEvent(mEventSource, mSession, this, mData.mOffCurrent,
+ cbRead, ComSafeArrayAsInParam(data));
+ }
+ else
+ vrc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ case GUEST_FILE_NOTIFYTYPE_WRITE:
+ {
+ if (pSvcCbData->mParms == 4)
+ {
+ pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.u.write.cbWritten);
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ mData.mOffCurrent += dataCb.u.write.cbWritten;
+ uint64_t uOffCurrent = mData.mOffCurrent;
+
+ alock.release();
+
+ fireGuestFileWriteEvent(mEventSource, mSession, this, uOffCurrent,
+ dataCb.u.write.cbWritten);
+ }
+ else
+ vrc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ case GUEST_FILE_NOTIFYTYPE_SEEK:
+ {
+ if (pSvcCbData->mParms == 4)
+ {
+ pSvcCbData->mpaParms[idx++].getUInt64(&dataCb.u.seek.uOffActual);
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ mData.mOffCurrent = dataCb.u.seek.uOffActual;
+
+ alock.release();
+
+ fireGuestFileOffsetChangedEvent(mEventSource, mSession, this,
+ dataCb.u.seek.uOffActual, 0 /* Processed */);
+ }
+ else
+ vrc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ case GUEST_FILE_NOTIFYTYPE_TELL:
+ {
+ if (pSvcCbData->mParms == 4)
+ {
+ pSvcCbData->mpaParms[idx++].getUInt64(&dataCb.u.tell.uOffActual);
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ mData.mOffCurrent = dataCb.u.tell.uOffActual;
+
+ alock.release();
+
+ fireGuestFileOffsetChangedEvent(mEventSource, mSession, this,
+ dataCb.u.tell.uOffActual, 0 /* Processed */);
+ }
+ else
+ vrc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ default:
+ vrc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ if (RT_SUCCESS(vrc))
+ {
+ GuestWaitEventPayload payload(dataCb.uType, &dataCb, sizeof(dataCb));
+ int rc2 = signalWaitEventInternal(pCbCtx, guestRc, &payload);
+ AssertRC(rc2);
+ }
+
+ LogFlowThisFunc(("uType=%RU32, guestRc=%Rrc\n",
+ dataCb.uType, dataCb.rc));
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestFile::onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
+{
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
+
+ int vrc = setFileStatus(FileStatus_Down, VINF_SUCCESS);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+/**
+ * Called by IGuestSession right before this file gets removed
+ * from the public file list.
+ */
+int GuestFile::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;
+}
+
+int GuestFile::openFile(uint32_t uTimeoutMS, int *pGuestRc)
+{
+ LogFlowThisFuncEnter();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ LogFlowThisFunc(("strFile=%s, strOpenMode=%s, strDisposition=%s, uCreationMode=%RU32, uOffset=%RU64\n",
+ mData.mOpenInfo.mFileName.c_str(), mData.mOpenInfo.mOpenMode.c_str(),
+ mData.mOpenInfo.mDisposition.c_str(), mData.mOpenInfo.mCreationMode, mData.mOpenInfo.mInitialOffset));
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
+ try
+ {
+ eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
+
+ vrc = registerWaitEvent(eventTypes, &pEvent);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /* Prepare HGCM call. */
+ VBOXHGCMSVCPARM paParms[8];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setPointer((void*)mData.mOpenInfo.mFileName.c_str(),
+ (ULONG)mData.mOpenInfo.mFileName.length() + 1);
+ paParms[i++].setPointer((void*)mData.mOpenInfo.mOpenMode.c_str(),
+ (ULONG)mData.mOpenInfo.mOpenMode.length() + 1);
+ paParms[i++].setPointer((void*)mData.mOpenInfo.mDisposition.c_str(),
+ (ULONG)mData.mOpenInfo.mDisposition.length() + 1);
+ paParms[i++].setPointer((void*)mData.mOpenInfo.mSharingMode.c_str(),
+ (ULONG)mData.mOpenInfo.mSharingMode.length() + 1);
+ paParms[i++].setUInt32(mData.mOpenInfo.mCreationMode);
+ paParms[i++].setUInt64(mData.mOpenInfo.mInitialOffset);
+
+ alock.release(); /* Drop write lock before sending. */
+
+ vrc = sendCommand(HOST_FILE_OPEN, i, paParms);
+ if (RT_SUCCESS(vrc))
+ vrc = waitForStatusChange(pEvent, uTimeoutMS,
+ NULL /* FileStatus */, pGuestRc);
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestFile::readData(uint32_t uSize, uint32_t uTimeoutMS,
+ void* pvData, uint32_t cbData, uint32_t* pcbRead)
+{
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+ LogFlowThisFunc(("uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
+ uSize, uTimeoutMS, pvData, cbData));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
+ try
+ {
+ eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
+ eventTypes.push_back(VBoxEventType_OnGuestFileRead);
+
+ vrc = registerWaitEvent(eventTypes, &pEvent);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /* Prepare HGCM call. */
+ VBOXHGCMSVCPARM paParms[4];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setUInt32(mData.mID /* File handle */);
+ paParms[i++].setUInt32(uSize /* Size (in bytes) to read */);
+
+ alock.release(); /* Drop write lock before sending. */
+
+ uint32_t cbRead;
+ vrc = sendCommand(HOST_FILE_READ, i, paParms);
+ if (RT_SUCCESS(vrc))
+ vrc = waitForRead(pEvent, uTimeoutMS, pvData, cbData, &cbRead);
+
+ if (RT_SUCCESS(vrc))
+ {
+ LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
+
+ if (pcbRead)
+ *pcbRead = cbRead;
+ }
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestFile::readDataAt(uint64_t uOffset, uint32_t uSize, uint32_t uTimeoutMS,
+ void* pvData, size_t cbData, size_t* pcbRead)
+{
+ LogFlowThisFunc(("uOffset=%RU64, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
+ uOffset, uSize, uTimeoutMS, pvData, cbData));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
+ try
+ {
+ eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
+ eventTypes.push_back(VBoxEventType_OnGuestFileRead);
+
+ vrc = registerWaitEvent(eventTypes, &pEvent);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /* Prepare HGCM call. */
+ VBOXHGCMSVCPARM paParms[4];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setUInt32(mData.mID /* File handle */);
+ paParms[i++].setUInt64(uOffset /* Offset (in bytes) to start reading */);
+ paParms[i++].setUInt32(uSize /* Size (in bytes) to read */);
+
+ alock.release(); /* Drop write lock before sending. */
+
+ uint32_t cbRead;
+ vrc = sendCommand(HOST_FILE_READ_AT, i, paParms);
+ if (RT_SUCCESS(vrc))
+ vrc = waitForRead(pEvent, uTimeoutMS, pvData, cbData, &cbRead);
+
+ if (RT_SUCCESS(vrc))
+ {
+ LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
+
+ if (pcbRead)
+ *pcbRead = cbRead;
+ }
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestFile::seekAt(int64_t iOffset, GUEST_FILE_SEEKTYPE eSeekType,
+ uint32_t uTimeoutMS, uint64_t *puOffset)
+{
+ LogFlowThisFunc(("iOffset=%RI64, uTimeoutMS=%RU32\n",
+ iOffset, uTimeoutMS));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
+ try
+ {
+ eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
+ eventTypes.push_back(VBoxEventType_OnGuestFileOffsetChanged);
+
+ vrc = registerWaitEvent(eventTypes, &pEvent);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /* Prepare HGCM call. */
+ VBOXHGCMSVCPARM paParms[4];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setUInt32(mData.mID /* File handle */);
+ paParms[i++].setUInt32(eSeekType /* Seek method */);
+ /** @todo uint64_t vs. int64_t! */
+ paParms[i++].setUInt64((uint64_t)iOffset /* Offset (in bytes) to start reading */);
+
+ alock.release(); /* Drop write lock before sending. */
+
+ vrc = sendCommand(HOST_FILE_SEEK, i, paParms);
+ if (RT_SUCCESS(vrc))
+ vrc = waitForOffsetChange(pEvent, uTimeoutMS, puOffset);
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
}
/* static */
-uint32_t GuestFile::getOpenModeFromString(const Utf8Str &strOpenMode)
+HRESULT GuestFile::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, GuestFile::guestErrorToString(guestRc).c_str());
+}
+
+int GuestFile::setFileStatus(FileStatus_T fileStatus, int fileRc)
+{
+ LogFlowThisFuncEnter();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, fileRc=%Rrc\n",
+ mData.mStatus, fileStatus, fileRc));
+
+#ifdef VBOX_STRICT
+ if (fileStatus == FileStatus_Error)
+ {
+ AssertMsg(RT_FAILURE(fileRc), ("Guest rc must be an error (%Rrc)\n", fileRc));
+ }
+ else
+ AssertMsg(RT_SUCCESS(fileRc), ("Guest rc must not be an error (%Rrc)\n", fileRc));
+#endif
+
+ if (mData.mStatus != fileStatus)
+ {
+ mData.mStatus = fileStatus;
+ mData.mLastError = fileRc;
+
+ ComObjPtr<VirtualBoxErrorInfo> errorInfo;
+ HRESULT hr = errorInfo.createObject();
+ ComAssertComRC(hr);
+ if (RT_FAILURE(fileRc))
+ {
+ hr = errorInfo->initEx(VBOX_E_IPRT_ERROR, fileRc,
+ COM_IIDOF(IGuestFile), getComponentName(),
+ guestErrorToString(fileRc));
+ ComAssertComRC(hr);
+ }
+
+ alock.release(); /* Release lock before firing off event. */
+
+ fireGuestFileStateChangedEvent(mEventSource, mSession,
+ this, fileStatus, errorInfo);
+ }
+
+ return VINF_SUCCESS;
+}
+
+int GuestFile::waitForOffsetChange(GuestWaitEvent *pEvent,
+ uint32_t uTimeoutMS, uint64_t *puOffset)
+{
+ AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+
+ VBoxEventType_T evtType;
+ ComPtr<IEvent> pIEvent;
+ int vrc = waitForEvent(pEvent, uTimeoutMS,
+ &evtType, pIEvent.asOutParam());
+ if (RT_SUCCESS(vrc))
+ {
+ if (evtType == VBoxEventType_OnGuestFileOffsetChanged)
+ {
+ if (puOffset)
+ {
+ ComPtr<IGuestFileOffsetChangedEvent> pFileEvent = pIEvent;
+ Assert(!pFileEvent.isNull());
+
+ HRESULT hr = pFileEvent->COMGETTER(Offset)((LONG64*)puOffset);
+ ComAssertComRC(hr);
+ }
+ }
+ else
+ vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
+ }
+
+ return vrc;
+}
+
+int GuestFile::waitForRead(GuestWaitEvent *pEvent,
+ uint32_t uTimeoutMS,
+ void *pvData, size_t cbData, uint32_t *pcbRead)
+{
+ AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+
+ VBoxEventType_T evtType;
+ ComPtr<IEvent> pIEvent;
+ int vrc = waitForEvent(pEvent, uTimeoutMS,
+ &evtType, pIEvent.asOutParam());
+ if (RT_SUCCESS(vrc))
+ {
+ if (evtType == VBoxEventType_OnGuestFileRead)
+ {
+ ComPtr<IGuestFileReadEvent> pFileEvent = pIEvent;
+ Assert(!pFileEvent.isNull());
+
+ HRESULT hr;
+ if (pvData)
+ {
+ com::SafeArray <BYTE> data;
+ hr = pFileEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data));
+ ComAssertComRC(hr);
+ size_t cbRead = data.size();
+ if ( cbRead
+ && cbRead <= cbData)
+ {
+ memcpy(pvData, data.raw(), data.size());
+ }
+ else
+ vrc = VERR_BUFFER_OVERFLOW;
+ }
+ if (pcbRead)
+ {
+ hr = pFileEvent->COMGETTER(Processed)((ULONG*)pcbRead);
+ ComAssertComRC(hr);
+ }
+ }
+ else
+ vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
+ }
+
+ return vrc;
+}
+
+int GuestFile::waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
+ FileStatus_T *pFileStatus, int *pGuestRc)
+{
+ AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+ /* pFileStatus is optional. */
+
+ VBoxEventType_T evtType;
+ ComPtr<IEvent> pIEvent;
+ int vrc = waitForEvent(pEvent, uTimeoutMS,
+ &evtType, pIEvent.asOutParam());
+ if (RT_SUCCESS(vrc))
+ {
+ Assert(evtType == VBoxEventType_OnGuestFileStateChanged);
+ ComPtr<IGuestFileStateChangedEvent> pFileEvent = pIEvent;
+ Assert(!pFileEvent.isNull());
+
+ HRESULT hr;
+ if (pFileStatus)
+ {
+ hr = pFileEvent->COMGETTER(Status)(pFileStatus);
+ ComAssertComRC(hr);
+ }
+
+ ComPtr<IVirtualBoxErrorInfo> errorInfo;
+ hr = pFileEvent->COMGETTER(Error)(errorInfo.asOutParam());
+ ComAssertComRC(hr);
+
+ LONG lGuestRc;
+ hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
+ ComAssertComRC(hr);
+
+ LogFlowThisFunc(("resultDetail=%RI32 (%Rrc)\n",
+ lGuestRc, lGuestRc));
+
+ if (RT_FAILURE((int)lGuestRc))
+ vrc = VERR_GSTCTL_GUEST_ERROR;
+
+ if (pGuestRc)
+ *pGuestRc = (int)lGuestRc;
+ }
+
+ return vrc;
+}
+
+int GuestFile::waitForWrite(GuestWaitEvent *pEvent,
+ uint32_t uTimeoutMS, uint32_t *pcbWritten)
{
- return 0; /** @todo Implement me! */
+ AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+
+ VBoxEventType_T evtType;
+ ComPtr<IEvent> pIEvent;
+ int vrc = waitForEvent(pEvent, uTimeoutMS,
+ &evtType, pIEvent.asOutParam());
+ if (RT_SUCCESS(vrc))
+ {
+ if (evtType == VBoxEventType_OnGuestFileWrite)
+ {
+ if (pcbWritten)
+ {
+ ComPtr<IGuestFileWriteEvent> pFileEvent = pIEvent;
+ Assert(!pFileEvent.isNull());
+
+ HRESULT hr = pFileEvent->COMGETTER(Processed)((ULONG*)pcbWritten);
+ ComAssertComRC(hr);
+ }
+ }
+ else
+ vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
+ }
+
+ return vrc;
+}
+
+int GuestFile::writeData(uint32_t uTimeoutMS, void *pvData, uint32_t cbData,
+ uint32_t *pcbWritten)
+{
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+ LogFlowThisFunc(("uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
+ uTimeoutMS, pvData, cbData));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
+ try
+ {
+ eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
+ eventTypes.push_back(VBoxEventType_OnGuestFileWrite);
+
+ vrc = registerWaitEvent(eventTypes, &pEvent);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /* Prepare HGCM call. */
+ VBOXHGCMSVCPARM paParms[8];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setUInt32(mData.mID /* File handle */);
+ paParms[i++].setUInt32(cbData /* Size (in bytes) to write */);
+ paParms[i++].setPointer(pvData, cbData);
+
+ alock.release(); /* Drop write lock before sending. */
+
+ uint32_t cbWritten;
+ vrc = sendCommand(HOST_FILE_WRITE, i, paParms);
+ if (RT_SUCCESS(vrc))
+ vrc = waitForWrite(pEvent, uTimeoutMS, &cbWritten);
+
+ if (RT_SUCCESS(vrc))
+ {
+ LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
+
+ if (cbWritten)
+ *pcbWritten = cbWritten;
+ }
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestFile::writeDataAt(uint64_t uOffset, uint32_t uTimeoutMS,
+ void *pvData, uint32_t cbData, uint32_t *pcbWritten)
+{
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+ LogFlowThisFunc(("uOffset=%RU64, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
+ uOffset, uTimeoutMS, pvData, cbData));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
+ try
+ {
+ eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
+ eventTypes.push_back(VBoxEventType_OnGuestFileWrite);
+
+ vrc = registerWaitEvent(eventTypes, &pEvent);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /* Prepare HGCM call. */
+ VBOXHGCMSVCPARM paParms[8];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setUInt32(mData.mID /* File handle */);
+ paParms[i++].setUInt64(uOffset /* Offset where to starting writing */);
+ paParms[i++].setUInt32(cbData /* Size (in bytes) to write */);
+ paParms[i++].setPointer(pvData, cbData);
+
+ alock.release(); /* Drop write lock before sending. */
+
+ uint32_t cbWritten;
+ vrc = sendCommand(HOST_FILE_WRITE_AT, i, paParms);
+ if (RT_SUCCESS(vrc))
+ vrc = waitForWrite(pEvent, uTimeoutMS, &cbWritten);
+
+ if (RT_SUCCESS(vrc))
+ {
+ LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
+
+ if (cbWritten)
+ *pcbWritten = cbWritten;
+ }
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
}
// implementation of public methods
@@ -238,17 +1278,27 @@ STDMETHODIMP GuestFile::Close(void)
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- AssertPtr(mData.mSession);
- int rc = mData.mSession->fileRemoveFromList(this);
+ /* Close file on guest. */
+ int guestRc;
+ int rc = closeFile(&guestRc);
+ /* On failure don't return here, instead do all the cleanup
+ * work first and then return an error. */
- /*
- * Release autocaller before calling uninit.
- */
- autoCaller.release();
+ AssertPtr(mSession);
+ int rc2 = mSession->fileRemoveFromList(this);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
- uninit();
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_GSTCTL_GUEST_ERROR)
+ return GuestFile::setErrorExternal(this, guestRc);
+
+ return setError(VBOX_E_IPRT_ERROR,
+ tr("Closing guest file failed with %Rrc\n"), rc);
+ }
- LogFlowFuncLeaveRC(rc);
+ LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
return S_OK;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -270,10 +1320,41 @@ STDMETHODIMP GuestFile::Read(ULONG aToRead, ULONG aTimeoutMS, ComSafeArrayOut(BY
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
#else
+ if (aToRead == 0)
+ return setError(E_INVALIDARG, tr("The size to read is zero"));
+ CheckComArgOutSafeArrayPointerValid(aData);
+
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- ReturnComNotImplemented();
+ com::SafeArray<BYTE> data((size_t)aToRead);
+ Assert(data.size() >= aToRead);
+
+ HRESULT hr = S_OK;
+
+ uint32_t cbRead;
+ int vrc = readData(aToRead, aTimeoutMS,
+ data.raw(), aToRead, &cbRead);
+ if (RT_SUCCESS(vrc))
+ {
+ if (data.size() != cbRead)
+ data.resize(cbRead);
+ data.detachTo(ComSafeArrayOutArg(aData));
+ }
+ else
+ {
+ switch (vrc)
+ {
+ default:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Reading from file \"%s\" failed: %Rrc"),
+ mData.mOpenInfo.mFileName.c_str(), vrc);
+ break;
+ }
+ }
+
+ LogFlowFuncLeaveRC(vrc);
+ return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -282,10 +1363,41 @@ STDMETHODIMP GuestFile::ReadAt(LONG64 aOffset, ULONG aToRead, ULONG aTimeoutMS,
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
#else
+ if (aToRead == 0)
+ return setError(E_INVALIDARG, tr("The size to read is zero"));
+ CheckComArgOutSafeArrayPointerValid(aData);
+
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- ReturnComNotImplemented();
+ com::SafeArray<BYTE> data((size_t)aToRead);
+ Assert(data.size() >= aToRead);
+
+ HRESULT hr = S_OK;
+
+ size_t cbRead;
+ int vrc = readDataAt(aOffset, aToRead, aTimeoutMS,
+ data.raw(), aToRead, &cbRead);
+ if (RT_SUCCESS(vrc))
+ {
+ if (data.size() != cbRead)
+ data.resize(cbRead);
+ data.detachTo(ComSafeArrayOutArg(aData));
+ }
+ else
+ {
+ switch (vrc)
+ {
+ default:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Reading from file \"%s\" (at offset %RU64) failed: %Rrc"),
+ mData.mOpenInfo.mFileName.c_str(), aOffset, vrc);
+ break;
+ }
+ }
+
+ LogFlowFuncLeaveRC(vrc);
+ return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -294,10 +1406,45 @@ STDMETHODIMP GuestFile::Seek(LONG64 aOffset, FileSeekType_T aType)
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
#else
+ LogFlowThisFuncEnter();
+
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- ReturnComNotImplemented();
+ HRESULT hr = S_OK;
+
+ GUEST_FILE_SEEKTYPE eSeekType;
+ switch (aType)
+ {
+ case FileSeekType_Set:
+ eSeekType = GUEST_FILE_SEEKTYPE_BEGIN;
+ break;
+
+ case FileSeekType_Current:
+ eSeekType = GUEST_FILE_SEEKTYPE_CURRENT;
+ break;
+
+ default:
+ return setError(E_INVALIDARG, tr("Invalid seek type specified"));
+ break; /* Never reached. */
+ }
+
+ int vrc = seekAt(aOffset, eSeekType,
+ 30 * 1000 /* 30s timeout */, NULL /* puOffset */);
+ if (RT_FAILURE(vrc))
+ {
+ switch (vrc)
+ {
+ default:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Seeking file \"%s\" (to offset %RI64) failed: %Rrc"),
+ mData.mOpenInfo.mFileName.c_str(), aOffset, vrc);
+ break;
+ }
+ }
+
+ LogFlowFuncLeaveRC(vrc);
+ return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -318,10 +1465,33 @@ STDMETHODIMP GuestFile::Write(ComSafeArrayIn(BYTE, aData), ULONG aTimeoutMS, ULO
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
#else
+ LogFlowThisFuncEnter();
+
+ CheckComArgSafeArrayNotNull(aData);
+ CheckComArgOutPointerValid(aWritten);
+
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- ReturnComNotImplemented();
+ HRESULT hr = S_OK;
+
+ com::SafeArray<BYTE> data(ComSafeArrayInArg(aData));
+ int vrc = writeData(aTimeoutMS, data.raw(), (uint32_t)data.size(),
+ (uint32_t*)aWritten);
+ if (RT_FAILURE(vrc))
+ {
+ switch (vrc)
+ {
+ default:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Writing %zubytes to file \"%s\" failed: %Rrc"),
+ data.size(), mData.mOpenInfo.mFileName.c_str(), vrc);
+ break;
+ }
+ }
+
+ LogFlowFuncLeaveRC(vrc);
+ return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
@@ -330,10 +1500,33 @@ STDMETHODIMP GuestFile::WriteAt(LONG64 aOffset, ComSafeArrayIn(BYTE, aData), ULO
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
#else
+ LogFlowThisFuncEnter();
+
+ CheckComArgSafeArrayNotNull(aData);
+ CheckComArgOutPointerValid(aWritten);
+
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- ReturnComNotImplemented();
+ HRESULT hr = S_OK;
+
+ com::SafeArray<BYTE> data(ComSafeArrayInArg(aData));
+ int vrc = writeData(aTimeoutMS, data.raw(), (uint32_t)data.size(),
+ (uint32_t*)aWritten);
+ if (RT_FAILURE(vrc))
+ {
+ switch (vrc)
+ {
+ default:
+ hr = setError(VBOX_E_IPRT_ERROR,
+ tr("Writing %zubytes to file \"%s\" (at offset %RU64) failed: %Rrc"),
+ data.size(), mData.mOpenInfo.mFileName.c_str(), aOffset, vrc);
+ break;
+ }
+ }
+
+ LogFlowFuncLeaveRC(vrc);
+ return hr;
#endif /* VBOX_WITH_GUEST_CONTROL */
}
diff --git a/src/VBox/Main/src-client/GuestFsObjInfoImpl.cpp b/src/VBox/Main/src-client/GuestFsObjInfoImpl.cpp
index 99219da7..64547edc 100644
--- a/src/VBox/Main/src-client/GuestFsObjInfoImpl.cpp
+++ b/src/VBox/Main/src-client/GuestFsObjInfoImpl.cpp
@@ -1,7 +1,6 @@
-
/* $Id: GuestFsObjInfoImpl.cpp $ */
/** @file
- * VirtualBox Main - XXX.
+ * VirtualBox Main - Guest file system object information handling.
*/
/*
diff --git a/src/VBox/Main/src-client/GuestImpl.cpp b/src/VBox/Main/src-client/GuestImpl.cpp
index 4f096e4e..41469b0c 100644
--- a/src/VBox/Main/src-client/GuestImpl.cpp
+++ b/src/VBox/Main/src-client/GuestImpl.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;
@@ -29,9 +29,12 @@
#include "AutoCaller.h"
#include "Logging.h"
#include "Performance.h"
+#include "VBoxEvents.h"
#include <VBox/VMMDev.h>
#include <iprt/cpp/utils.h>
+#include <iprt/ctype.h>
+#include <iprt/stream.h>
#include <iprt/timer.h>
#include <VBox/vmm/pgm.h>
#include <VBox/version.h>
@@ -77,31 +80,41 @@ HRESULT Guest::init(Console *aParent)
autoInitSpan.setSucceeded();
ULONG aMemoryBalloonSize;
- HRESULT ret = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
- if (ret == S_OK)
+ HRESULT hr = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
+ if (hr == S_OK) /** @todo r=andy SUCCEEDED? */
mMemoryBalloonSize = aMemoryBalloonSize;
else
- mMemoryBalloonSize = 0; /* Default is no ballooning */
+ mMemoryBalloonSize = 0; /* Default is no ballooning */
BOOL fPageFusionEnabled;
- ret = mParent->machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled);
- if (ret == S_OK)
+ hr = mParent->machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled);
+ if (hr == S_OK) /** @todo r=andy SUCCEEDED? */
mfPageFusionEnabled = fPageFusionEnabled;
else
- mfPageFusionEnabled = false; /* Default is no page fusion*/
+ mfPageFusionEnabled = false; /* Default is no page fusion*/
- mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */
+ mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */
mCollectVMMStats = false;
/* Clear statistics. */
+ mNetStatRx = mNetStatTx = 0;
+ mNetStatLastTs = RTTimeNanoTS();
for (unsigned i = 0 ; i < GUESTSTATTYPE_MAX; i++)
mCurrentGuestStat[i] = 0;
- mGuestValidStats = pm::GUESTSTATMASK_NONE;
+ mVmValidStats = pm::VMSTATMASK_NONE;
mMagic = GUEST_MAGIC;
int vrc = RTTimerLRCreate(&mStatTimer, 1000 /* ms */,
&Guest::staticUpdateStats, this);
- AssertMsgRC(vrc, ("Failed to create guest statistics update timer(%Rra)\n", vrc));
+ AssertMsgRC(vrc, ("Failed to create guest statistics update timer (%Rrc)\n", vrc));
+
+#ifdef VBOX_WITH_GUEST_CONTROL
+ hr = unconst(mEventSource).createObject();
+ if (SUCCEEDED(hr))
+ hr = mEventSource->init();
+#else
+ hr = S_OK;
+#endif
try
{
@@ -112,10 +125,10 @@ HRESULT Guest::init(Console *aParent)
}
catch(std::bad_alloc &)
{
- return E_OUTOFMEMORY;
+ hr = E_OUTOFMEMORY;
}
- return S_OK;
+ return hr;
}
/**
@@ -145,7 +158,7 @@ void Guest::uninit()
{
#ifdef DEBUG
ULONG cRefs = itSessions->second->AddRef();
- LogFlowThisFunc(("pSession=%p, cRefs=%RU32\n", (GuestSession *)itSessions->second, cRefs > 0 ? cRefs - 1 : 0));
+ LogFlowThisFunc(("sessionID=%RU32, cRefs=%RU32\n", itSessions->first, cRefs > 1 ? cRefs - 1 : 0));
itSessions->second->Release();
#endif
itSessions->second->uninit();
@@ -162,13 +175,16 @@ void Guest::uninit()
}
#endif
+#ifdef VBOX_WITH_GUEST_CONTROL
+ unconst(mEventSource).setNull();
+#endif
unconst(mParent) = NULL;
LogFlowFuncLeave();
}
/* static */
-void Guest::staticUpdateStats(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick)
+DECLCALLBACK(void) Guest::staticUpdateStats(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick)
{
AssertReturnVoid(pvUser != NULL);
Guest *guest = static_cast<Guest *>(pvUser);
@@ -179,84 +195,155 @@ void Guest::staticUpdateStats(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick)
NOREF(hTimerLR);
}
+/* static */
+int Guest::staticEnumStatsCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit,
+ STAMVISIBILITY enmVisiblity, const char *pszDesc, void *pvUser)
+{
+ AssertLogRelMsgReturn(enmType == STAMTYPE_COUNTER, ("Unexpected sample type %d ('%s')\n", enmType, pszName), VINF_SUCCESS);
+ AssertLogRelMsgReturn(enmUnit == STAMUNIT_BYTES, ("Unexpected sample unit %d ('%s')\n", enmUnit, pszName), VINF_SUCCESS);
+
+ /* Get the base name w/ slash. */
+ const char *pszLastSlash = strrchr(pszName, '/');
+ AssertLogRelMsgReturn(pszLastSlash, ("Unexpected sample '%s'\n", pszName), VINF_SUCCESS);
+
+ /* Receive or transmit? */
+ bool fRx;
+ if (!strcmp(pszLastSlash, "/BytesReceived"))
+ fRx = true;
+ else if (!strcmp(pszLastSlash, "/BytesTransmitted"))
+ fRx = false;
+ else
+ AssertLogRelMsgFailedReturn(("Unexpected sample '%s'\n", pszName), VINF_SUCCESS);
+
+#if 0 /* not used for anything, so don't bother parsing it. */
+ /* Find start of instance number. ASSUMES '/Public/Net/Name<Instance digits>/Bytes...' */
+ do
+ --pszLastSlash;
+ while (pszLastSlash > pszName && RT_C_IS_DIGIT(*pszLastSlash));
+ pszLastSlash++;
+
+ uint8_t uInstance;
+ int rc = RTStrToUInt8Ex(pszLastSlash, NULL, 10, &uInstance);
+ AssertLogRelMsgReturn(RT_SUCCESS(rc) && rc != VWRN_NUMBER_TOO_BIG && rc != VWRN_NEGATIVE_UNSIGNED,
+ ("%Rrc '%s'\n", rc, pszName), VINF_SUCCESS)
+#endif
+
+ /* Add the bytes to our counters. */
+ PSTAMCOUNTER pCnt = (PSTAMCOUNTER)pvSample;
+ Guest *pGuest = (Guest *)pvUser;
+ uint64_t cb = pCnt->c;
+#if 0
+ LogFlowFunc(("%s i=%u d=%s %llu bytes\n", pszName, uInstance, fRx ? "RX" : "TX", cb));
+#else
+ LogFlowFunc(("%s d=%s %llu bytes\n", pszName, fRx ? "RX" : "TX", cb));
+#endif
+ if (fRx)
+ pGuest->mNetStatRx += cb;
+ else
+ pGuest->mNetStatTx += cb;
+
+ return VINF_SUCCESS;
+}
+
void Guest::updateStats(uint64_t iTick)
{
- uint64_t uFreeTotal, uAllocTotal, uBalloonedTotal, uSharedTotal;
- uint64_t uTotalMem, uPrivateMem, uSharedMem, uZeroMem;
+ uint64_t cbFreeTotal = 0;
+ uint64_t cbAllocTotal = 0;
+ uint64_t cbBalloonedTotal = 0;
+ uint64_t cbSharedTotal = 0;
+ uint64_t cbSharedMem = 0;
+ ULONG uNetStatRx = 0;
+ ULONG uNetStatTx = 0;
+ ULONG aGuestStats[GUESTSTATTYPE_MAX];
+ RT_ZERO(aGuestStats);
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- ULONG aGuestStats[GUESTSTATTYPE_MAX];
- RT_ZERO(aGuestStats);
- ULONG validStats = mGuestValidStats;
+ ULONG validStats = mVmValidStats;
/* Check if we have anything to report */
if (validStats)
{
- mGuestValidStats = pm::GUESTSTATMASK_NONE;
+ mVmValidStats = pm::VMSTATMASK_NONE;
memcpy(aGuestStats, mCurrentGuestStat, sizeof(aGuestStats));
}
alock.release();
+
/*
* Calling SessionMachine may take time as the object resides in VBoxSVC
* process. This is why we took a snapshot of currently collected stats
* and released the lock.
*/
- uFreeTotal = 0;
- uAllocTotal = 0;
- uBalloonedTotal = 0;
- uSharedTotal = 0;
- uTotalMem = 0;
- uPrivateMem = 0;
- uSharedMem = 0;
- uZeroMem = 0;
-
- Console::SafeVMPtr pVM(mParent);
- if (pVM.isOk())
+ Console::SafeVMPtrQuiet ptrVM(mParent);
+ if (ptrVM.isOk())
{
int rc;
/*
* There is no point in collecting VM shared memory if other memory
- * statistics are not available yet. Or is it?
+ * statistics are not available yet. Or is there?
*/
if (validStats)
{
/* Query the missing per-VM memory statistics. */
- rc = PGMR3QueryMemoryStats(pVM.raw(), &uTotalMem, &uPrivateMem, &uSharedMem, &uZeroMem);
+ uint64_t cbTotalMemIgn, cbPrivateMemIgn, cbZeroMemIgn;
+ rc = PGMR3QueryMemoryStats(ptrVM.rawUVM(), &cbTotalMemIgn, &cbPrivateMemIgn, &cbSharedMem, &cbZeroMemIgn);
if (rc == VINF_SUCCESS)
- {
- validStats |= pm::GUESTSTATMASK_MEMSHARED;
- }
+ validStats |= pm::VMSTATMASK_GUEST_MEMSHARED;
}
if (mCollectVMMStats)
{
- rc = PGMR3QueryGlobalMemoryStats(pVM.raw(), &uAllocTotal, &uFreeTotal, &uBalloonedTotal, &uSharedTotal);
+ rc = PGMR3QueryGlobalMemoryStats(ptrVM.rawUVM(), &cbAllocTotal, &cbFreeTotal, &cbBalloonedTotal, &cbSharedTotal);
AssertRC(rc);
if (rc == VINF_SUCCESS)
- {
- validStats |= pm::GUESTSTATMASK_ALLOCVMM|pm::GUESTSTATMASK_FREEVMM|
- pm::GUESTSTATMASK_BALOONVMM|pm::GUESTSTATMASK_SHAREDVMM;
- }
+ validStats |= pm::VMSTATMASK_VMM_ALLOC | pm::VMSTATMASK_VMM_FREE
+ | pm::VMSTATMASK_VMM_BALOON | pm::VMSTATMASK_VMM_SHARED;
}
+ uint64_t uRxPrev = mNetStatRx;
+ uint64_t uTxPrev = mNetStatTx;
+ mNetStatRx = mNetStatTx = 0;
+ rc = STAMR3Enum(ptrVM.rawUVM(), "/Public/Net/*/Bytes*", staticEnumStatsCallback, this);
+ AssertRC(rc);
+
+ uint64_t uTsNow = RTTimeNanoTS();
+ uint64_t cNsPassed = uTsNow - mNetStatLastTs;
+ if (cNsPassed >= 1000)
+ {
+ mNetStatLastTs = uTsNow;
+
+ uNetStatRx = (ULONG)((mNetStatRx - uRxPrev) * 1000000 / (cNsPassed / 1000)); /* in bytes per second */
+ uNetStatTx = (ULONG)((mNetStatTx - uTxPrev) * 1000000 / (cNsPassed / 1000)); /* in bytes per second */
+ validStats |= pm::VMSTATMASK_NET_RX | pm::VMSTATMASK_NET_TX;
+ LogFlowThisFunc(("Net Rx=%llu Tx=%llu Ts=%llu Delta=%llu\n", mNetStatRx, mNetStatTx, uTsNow, cNsPassed));
+ }
+ else
+ {
+ /* Can happen on resume or if we're using a non-monotonic clock
+ source for the timer and the time is adjusted. */
+ mNetStatRx = uRxPrev;
+ mNetStatTx = uTxPrev;
+ LogThisFunc(("Net Ts=%llu cNsPassed=%llu - too small interval\n", uTsNow, cNsPassed));
+ }
}
- mParent->reportGuestStatistics(validStats,
- aGuestStats[GUESTSTATTYPE_CPUUSER],
- aGuestStats[GUESTSTATTYPE_CPUKERNEL],
- aGuestStats[GUESTSTATTYPE_CPUIDLE],
- /* Convert the units for RAM usage stats: page (4K) -> 1KB units */
- mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K),
- mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K),
- mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K),
- (ULONG)(uSharedMem / _1K), /* bytes -> KB */
- mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K),
- mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K),
- (ULONG)(uAllocTotal / _1K), /* bytes -> KB */
- (ULONG)(uFreeTotal / _1K),
- (ULONG)(uBalloonedTotal / _1K),
- (ULONG)(uSharedTotal / _1K));
+ mParent->reportVmStatistics(validStats,
+ aGuestStats[GUESTSTATTYPE_CPUUSER],
+ aGuestStats[GUESTSTATTYPE_CPUKERNEL],
+ aGuestStats[GUESTSTATTYPE_CPUIDLE],
+ /* Convert the units for RAM usage stats: page (4K) -> 1KB units */
+ mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K),
+ mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K),
+ mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K),
+ (ULONG)(cbSharedMem / _1K), /* bytes -> KB */
+ mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K),
+ mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K),
+ (ULONG)(cbAllocTotal / _1K), /* bytes -> KB */
+ (ULONG)(cbFreeTotal / _1K),
+ (ULONG)(cbBalloonedTotal / _1K),
+ (ULONG)(cbSharedTotal / _1K),
+ uNetStatRx,
+ uNetStatTx);
}
// IGuest properties
@@ -407,6 +494,26 @@ STDMETHODIMP Guest::COMGETTER(AdditionsRevision)(ULONG *a_puAdditionsRevision)
return hrc;
}
+STDMETHODIMP Guest::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);
+
+ LogFlowFuncLeaveRC(S_OK);
+ return S_OK;
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
STDMETHODIMP Guest::COMGETTER(Facilities)(ComSafeArrayOut(IAdditionsFacility *, aFacilities))
{
CheckComArgOutSafeArrayPointerValid(aFacilities);
@@ -570,47 +677,35 @@ STDMETHODIMP Guest::InternalGetStatistics(ULONG *aCpuUser, ULONG *aCpuKernel, UL
*aMemCache = mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K); /* page (4K) -> 1KB units */
*aPageTotal = mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
+ /* Play safe or smth? */
+ *aMemAllocTotal = 0;
+ *aMemFreeTotal = 0;
+ *aMemBalloonTotal = 0;
+ *aMemSharedTotal = 0;
+ *aMemShared = 0;
+
/* MUST release all locks before calling any PGM statistics queries,
* as they are executed by EMT and that might deadlock us by VMM device
* activity which waits for the Guest object lock. */
alock.release();
- Console::SafeVMPtr pVM (mParent);
- if (pVM.isOk())
- {
- uint64_t uFreeTotal, uAllocTotal, uBalloonedTotal, uSharedTotal;
- *aMemFreeTotal = 0;
- int rc = PGMR3QueryGlobalMemoryStats(pVM.raw(), &uAllocTotal, &uFreeTotal, &uBalloonedTotal, &uSharedTotal);
- AssertRC(rc);
- if (rc == VINF_SUCCESS)
- {
- *aMemAllocTotal = (ULONG)(uAllocTotal / _1K); /* bytes -> KB */
- *aMemFreeTotal = (ULONG)(uFreeTotal / _1K);
- *aMemBalloonTotal = (ULONG)(uBalloonedTotal / _1K);
- *aMemSharedTotal = (ULONG)(uSharedTotal / _1K);
- }
- else
- return E_FAIL;
-
- /* Query the missing per-VM memory statistics. */
- *aMemShared = 0;
- uint64_t uTotalMem, uPrivateMem, uSharedMem, uZeroMem;
- rc = PGMR3QueryMemoryStats(pVM.raw(), &uTotalMem, &uPrivateMem, &uSharedMem, &uZeroMem);
- if (rc == VINF_SUCCESS)
- {
- *aMemShared = (ULONG)(uSharedMem / _1K);
- }
- else
- return E_FAIL;
- }
- else
- {
- *aMemAllocTotal = 0;
- *aMemFreeTotal = 0;
- *aMemBalloonTotal = 0;
- *aMemSharedTotal = 0;
- *aMemShared = 0;
+ Console::SafeVMPtr ptrVM(mParent);
+ if (!ptrVM.isOk())
return E_FAIL;
- }
+
+ uint64_t cbFreeTotal, cbAllocTotal, cbBalloonedTotal, cbSharedTotal;
+ int rc = PGMR3QueryGlobalMemoryStats(ptrVM.rawUVM(), &cbAllocTotal, &cbFreeTotal, &cbBalloonedTotal, &cbSharedTotal);
+ AssertRCReturn(rc, E_FAIL);
+
+ *aMemAllocTotal = (ULONG)(cbAllocTotal / _1K); /* bytes -> KB */
+ *aMemFreeTotal = (ULONG)(cbFreeTotal / _1K);
+ *aMemBalloonTotal = (ULONG)(cbBalloonedTotal / _1K);
+ *aMemSharedTotal = (ULONG)(cbSharedTotal / _1K);
+
+ /* Query the missing per-VM memory statistics. */
+ uint64_t cbTotalMemIgn, cbPrivateMemIgn, cbSharedMem, cbZeroMemIgn;
+ rc = PGMR3QueryMemoryStats(ptrVM.rawUVM(), &cbTotalMemIgn, &cbPrivateMemIgn, &cbSharedMem, &cbZeroMemIgn);
+ AssertRCReturn(rc, E_FAIL);
+ *aMemShared = (ULONG)(cbSharedMem / _1K);
return S_OK;
}
@@ -619,15 +714,15 @@ HRESULT Guest::setStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal)
{
static ULONG indexToPerfMask[] =
{
- pm::GUESTSTATMASK_CPUUSER,
- pm::GUESTSTATMASK_CPUKERNEL,
- pm::GUESTSTATMASK_CPUIDLE,
- pm::GUESTSTATMASK_MEMTOTAL,
- pm::GUESTSTATMASK_MEMFREE,
- pm::GUESTSTATMASK_MEMBALLOON,
- pm::GUESTSTATMASK_MEMCACHE,
- pm::GUESTSTATMASK_PAGETOTAL,
- pm::GUESTSTATMASK_NONE
+ pm::VMSTATMASK_GUEST_CPUUSER,
+ pm::VMSTATMASK_GUEST_CPUKERNEL,
+ pm::VMSTATMASK_GUEST_CPUIDLE,
+ pm::VMSTATMASK_GUEST_MEMTOTAL,
+ pm::VMSTATMASK_GUEST_MEMFREE,
+ pm::VMSTATMASK_GUEST_MEMBALLOON,
+ pm::VMSTATMASK_GUEST_MEMCACHE,
+ pm::VMSTATMASK_GUEST_PAGETOTAL,
+ pm::VMSTATMASK_NONE
};
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
@@ -638,7 +733,7 @@ HRESULT Guest::setStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal)
return E_INVALIDARG;
mCurrentGuestStat[enmType] = aVal;
- mGuestValidStats |= indexToPerfMask[enmType];
+ mVmValidStats |= indexToPerfMask[enmType];
return S_OK;
}
@@ -1052,6 +1147,31 @@ void Guest::facilityUpdate(VBoxGuestFacilityType a_enmFacility, VBoxGuestFacilit
}
/**
+ * Issued by the guest when a guest user changed its state.
+ *
+ * @return IPRT status code.
+ * @param aUser Guest user name.
+ * @param aDomain Domain of guest user account. Optional.
+ * @param enmState New state to indicate.
+ * @param puDetails Pointer to state details. Optional.
+ * @param cbDetails Size (in bytes) of state details. Pass 0 if not used.
+ */
+void Guest::onUserStateChange(Bstr aUser, Bstr aDomain, VBoxGuestUserState enmState,
+ const uint8_t *puDetails, uint32_t cbDetails)
+{
+ LogFlowThisFunc(("\n"));
+
+ AutoCaller autoCaller(this);
+ AssertComRCReturnVoid(autoCaller.rc());
+
+ Bstr strDetails; /** @todo Implement state details here. */
+
+ fireGuestUserStateChangedEvent(mEventSource, aUser.raw(), aDomain.raw(),
+ (GuestUserState_T)enmState, strDetails.raw());
+ LogFlowFuncLeave();
+}
+
+/**
* Sets the status of a certain Guest Additions facility.
*
* Gets called by vmmdevUpdateGuestStatus, which just passes the report along.
diff --git a/src/VBox/Main/src-client/GuestProcessImpl.cpp b/src/VBox/Main/src-client/GuestProcessImpl.cpp
index feffba96..4ccf1e44 100644
--- a/src/VBox/Main/src-client/GuestProcessImpl.cpp
+++ b/src/VBox/Main/src-client/GuestProcessImpl.cpp
@@ -1,11 +1,10 @@
-
/* $Id: GuestProcessImpl.cpp $ */
/** @file
- * VirtualBox Main - XXX.
+ * VirtualBox Main - Guest process 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;
@@ -32,16 +31,20 @@
#include "GuestSessionImpl.h"
#include "GuestCtrlImplPrivate.h"
#include "ConsoleImpl.h"
+#include "VirtualBoxErrorInfoImpl.h"
#include "Global.h"
#include "AutoCaller.h"
-#include "VMMDev.h"
+#include "VBoxEvents.h"
#include <memory> /* For auto_ptr. */
#include <iprt/asm.h>
+#include <iprt/cpp/utils.h> /* For unconst(). */
#include <iprt/getopt.h>
-#include <VBox/VMMDev.h>
+
+#include <VBox/com/listeners.h>
+
#include <VBox/com/array.h>
#ifdef LOG_GROUP
@@ -79,6 +82,62 @@ public:
: GuestProcessTask(pProcess) { }
};
+/**
+ * Internal listener class to serve events in an
+ * active manner, e.g. without polling delays.
+ */
+class GuestProcessListener
+{
+public:
+
+ GuestProcessListener(void)
+ {
+ }
+
+ HRESULT init(GuestProcess *pProcess)
+ {
+ AssertPtrReturn(pProcess, E_POINTER);
+ mProcess = pProcess;
+ return S_OK;
+ }
+
+ void uninit(void)
+ {
+ mProcess = NULL;
+ }
+
+ STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
+ {
+ switch (aType)
+ {
+ case VBoxEventType_OnGuestProcessStateChanged:
+ case VBoxEventType_OnGuestProcessInputNotify:
+ case VBoxEventType_OnGuestProcessOutput:
+ {
+ AssertPtrReturn(mProcess, E_POINTER);
+ int rc2 = mProcess->signalWaitEvent(aType, aEvent);
+#ifdef DEBUG
+ LogFlowThisFunc(("Signalling events of type=%RU32, pProcess=%p resulted in rc=%Rrc\n",
+ aType, &mProcess, rc2));
+#endif
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("Unhandled event %RU32\n", aType));
+ break;
+ }
+
+ return S_OK;
+ }
+
+private:
+
+ GuestProcess *mProcess;
+};
+typedef ListenerImpl<GuestProcessListener, GuestProcess*> GuestProcessListenerImpl;
+
+VBOX_LISTENER_DECLARE(GuestProcessListenerImpl)
// constructor / destructor
/////////////////////////////////////////////////////////////////////////////
@@ -88,19 +147,7 @@ DEFINE_EMPTY_CTOR_DTOR(GuestProcess)
HRESULT GuestProcess::FinalConstruct(void)
{
LogFlowThisFuncEnter();
-
- mData.mExitCode = 0;
- mData.mNextContextID = 0;
- mData.mPID = 0;
- mData.mProcessID = 0;
- mData.mRC = VINF_SUCCESS;
- mData.mStatus = ProcessStatus_Undefined;
-
- mData.mWaitCount = 0;
- mData.mWaitEvent = NULL;
-
- HRESULT hr = BaseFinalConstruct();
- return hr;
+ return BaseFinalConstruct();
}
void GuestProcess::FinalRelease(void)
@@ -114,72 +161,124 @@ void GuestProcess::FinalRelease(void)
// public initializer/uninitializer for internal purposes only
/////////////////////////////////////////////////////////////////////////////
-int GuestProcess::init(Console *aConsole, GuestSession *aSession, ULONG aProcessID, const GuestProcessStartupInfo &aProcInfo)
+int GuestProcess::init(Console *aConsole, GuestSession *aSession,
+ ULONG aProcessID, const GuestProcessStartupInfo &aProcInfo)
{
LogFlowThisFunc(("aConsole=%p, aSession=%p, aProcessID=%RU32\n",
aConsole, aSession, aProcessID));
+ AssertPtrReturn(aConsole, VERR_INVALID_POINTER);
AssertPtrReturn(aSession, VERR_INVALID_POINTER);
/* Enclose the state transition NotReady->InInit->Ready. */
AutoInitSpan autoInitSpan(this);
AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
- mData.mConsole = aConsole;
- mData.mParent = aSession;
- mData.mProcessID = aProcessID;
- mData.mProcess = aProcInfo;
- /* Everything else will be set by the actual starting routine. */
-
- /* Confirm a successful initialization when it's the case. */
+#ifndef VBOX_WITH_GUEST_CONTROL
autoInitSpan.setSucceeded();
-
return VINF_SUCCESS;
+#else
+ HRESULT hr;
+
+ int vrc = bindToSession(aConsole, aSession, aProcessID /* Object ID */);
+ if (RT_SUCCESS(vrc))
+ {
+ hr = unconst(mEventSource).createObject();
+ if (FAILED(hr))
+ vrc = VERR_NO_MEMORY;
+ else
+ {
+ hr = mEventSource->init();
+ if (FAILED(hr))
+ vrc = VERR_COM_UNEXPECTED;
+ }
+ }
+
+ if (RT_SUCCESS(vrc))
+ {
+ try
+ {
+ GuestProcessListener *pListener = new GuestProcessListener();
+ ComObjPtr<GuestProcessListenerImpl> thisListener;
+ hr = thisListener.createObject();
+ if (SUCCEEDED(hr))
+ hr = thisListener->init(pListener, this);
+
+ if (SUCCEEDED(hr))
+ {
+ com::SafeArray <VBoxEventType_T> eventTypes;
+ eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
+ eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
+ eventTypes.push_back(VBoxEventType_OnGuestProcessOutput);
+ hr = mEventSource->RegisterListener(thisListener,
+ ComSafeArrayAsInParam(eventTypes),
+ TRUE /* Active listener */);
+ if (SUCCEEDED(hr))
+ {
+ vrc = baseInit();
+ if (RT_SUCCESS(vrc))
+ {
+ mLocalListener = thisListener;
+ }
+ }
+ else
+ vrc = VERR_COM_UNEXPECTED;
+ }
+ else
+ vrc = VERR_COM_UNEXPECTED;
+ }
+ catch(std::bad_alloc &)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+ }
+
+ if (RT_SUCCESS(vrc))
+ {
+ mData.mProcess = aProcInfo;
+ mData.mExitCode = 0;
+ mData.mPID = 0;
+ mData.mLastError = VINF_SUCCESS;
+ mData.mStatus = ProcessStatus_Undefined;
+ /* Everything else will be set by the actual starting routine. */
+
+ /* Confirm a successful initialization when it's the case. */
+ autoInitSpan.setSucceeded();
+
+ return vrc;
+ }
+
+ autoInitSpan.setFailed();
+ return vrc;
+#endif
}
/**
* Uninitializes the instance.
- * Called from FinalRelease().
+ * Called from FinalRelease() or IGuestSession::uninit().
*/
void GuestProcess::uninit(void)
{
- LogFlowThisFunc(("mCmd=%s, PID=%RU32\n",
- mData.mProcess.mCommand.c_str(), mData.mPID));
-
/* Enclose the state transition Ready->InUninit->NotReady. */
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
return;
- int vrc = VINF_SUCCESS;
-
#ifdef VBOX_WITH_GUEST_CONTROL
- /*
- * Cancel all callbacks + waiters.
- * Note: Deleting them is the job of the caller!
- */
- for (GuestCtrlCallbacks::iterator itCallbacks = mData.mCallbacks.begin();
- itCallbacks != mData.mCallbacks.end(); ++itCallbacks)
- {
- GuestCtrlCallback *pCallback = itCallbacks->second;
- AssertPtr(pCallback);
- int rc2 = pCallback->Cancel();
- if (RT_SUCCESS(vrc))
- vrc = rc2;
- }
- mData.mCallbacks.clear();
+ LogFlowThisFunc(("mCmd=%s, PID=%RU32\n",
+ mData.mProcess.mCommand.c_str(), mData.mPID));
- if (mData.mWaitEvent)
- {
- int rc2 = mData.mWaitEvent->Cancel();
- if (RT_SUCCESS(vrc))
- vrc = rc2;
- }
+ /* Terminate process if not already done yet. */
+ int guestRc = VINF_SUCCESS;
+ int vrc = terminateProcess(30 * 1000, &guestRc); /** @todo Make timeouts configurable. */
+ /* Note: Don't return here yet; first uninit all other stuff in
+ * case of failure. */
- mData.mStatus = ProcessStatus_Down; /** @todo Correct? */
-#endif
+ baseUninit();
- LogFlowFuncLeaveRC(vrc);
+ LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n",
+ vrc, guestRc));
+#endif
}
// implementation of public getters/setters for attributes
@@ -241,6 +340,26 @@ STDMETHODIMP GuestProcess::COMGETTER(Environment)(ComSafeArrayOut(BSTR, aEnviron
#endif /* VBOX_WITH_GUEST_CONTROL */
}
+STDMETHODIMP GuestProcess::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 */
+}
+
STDMETHODIMP GuestProcess::COMGETTER(ExecutablePath)(BSTR *aExecutablePath)
{
#ifndef VBOX_WITH_GUEST_CONTROL
@@ -342,143 +461,45 @@ STDMETHODIMP GuestProcess::COMGETTER(Status)(ProcessStatus_T *aStatus)
// private methods
/////////////////////////////////////////////////////////////////////////////
-inline int GuestProcess::callbackAdd(GuestCtrlCallback *pCallback, uint32_t *puContextID)
-{
- const ComObjPtr<GuestSession> pSession(mData.mParent);
- Assert(!pSession.isNull());
- ULONG uSessionID = 0;
- HRESULT hr = pSession->COMGETTER(Id)(&uSessionID);
- ComAssertComRC(hr);
-
- /* Create a new context ID and assign it. */
- int vrc = VERR_NOT_FOUND;
-
- ULONG uCount = mData.mNextContextID++;
- ULONG uNewContextID = 0;
- ULONG uTries = 0;
- for (;;)
- {
- if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
- uCount = 0;
-
- /* Create a new context ID ... */
- uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID,
- mData.mProcessID, uCount);
-
- /* Is the context ID already used? Try next ID ... */
- if (!callbackExists(uCount))
- {
- /* Callback with context ID was not found. This means
- * we can use this context ID for our new callback we want
- * to add below. */
- vrc = VINF_SUCCESS;
- break;
- }
-
- uCount++;
- if (++uTries == UINT32_MAX)
- break; /* Don't try too hard. */
- }
-
- if (RT_SUCCESS(vrc))
- {
- /* Add callback with new context ID to our callback map.
- * Note: This is *not* uNewContextID (which also includes
- * the session + process ID), just the context count
- * will be used here. */
- mData.mCallbacks[uCount] = pCallback;
- Assert(mData.mCallbacks.size());
-
- /* Report back new context ID. */
- if (puContextID)
- *puContextID = uNewContextID;
-
- LogFlowThisFunc(("Added new callback (Session: %RU32, Process: %RU32, Count=%RU32) CID=%RU32\n",
- uSessionID, mData.mProcessID, uCount, uNewContextID));
- }
-
- return vrc;
-}
-
-int GuestProcess::callbackDispatcher(uint32_t uContextID, uint32_t uFunction, void *pvData, size_t cbData)
+int GuestProcess::callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
{
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
#ifdef DEBUG
- LogFlowThisFunc(("uPID=%RU32, uContextID=%RU32, uFunction=%RU32, pvData=%p, cbData=%RU32\n",
- mData.mPID, uContextID, uFunction, pvData, cbData));
+ LogFlowThisFunc(("uPID=%RU32, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
+ mData.mPID, pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
#endif
- AssertPtrReturn(pvData, VERR_INVALID_POINTER);
- AssertReturn(cbData, VERR_INVALID_PARAMETER);
-
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
int vrc;
-
- /* Get the optional callback associated to this context ID.
- * The callback may not be around anymore if just kept locally by the caller when
- * doing the actual HGCM sending stuff. */
- GuestCtrlCallback *pCallback = NULL;
- GuestCtrlCallbacks::const_iterator it
- = mData.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
- if (it != mData.mCallbacks.end())
- {
- pCallback = it->second;
- AssertPtr(pCallback);
-#ifdef DEBUG
- LogFlowThisFunc(("pCallback=%p, CID=%RU32, Count=%RU32\n",
- pCallback, uContextID, VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID)));
-#endif
- }
-
- switch (uFunction)
+ switch (pCbCtx->uFunction)
{
case GUEST_DISCONNECTED:
{
- PCALLBACKDATACLIENTDISCONNECTED pCallbackData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvData);
- AssertPtr(pCallbackData);
- AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbData, VERR_INVALID_PARAMETER);
- AssertReturn(CALLBACKDATAMAGIC_CLIENT_DISCONNECTED == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
-
- vrc = onGuestDisconnected(pCallback, pCallbackData); /* Affects all callbacks. */
+ vrc = onGuestDisconnected(pCbCtx, pSvcCb);
break;
}
- case GUEST_EXEC_SEND_STATUS:
+ case GUEST_EXEC_STATUS:
{
- PCALLBACKDATAEXECSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvData);
- AssertPtr(pCallbackData);
- AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbData, VERR_INVALID_PARAMETER);
- AssertReturn(CALLBACKDATAMAGIC_EXEC_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
-
- vrc = onProcessStatusChange(pCallback, pCallbackData);
+ vrc = onProcessStatusChange(pCbCtx, pSvcCb);
break;
}
- case GUEST_EXEC_SEND_OUTPUT:
+ case GUEST_EXEC_OUTPUT:
{
- PCALLBACKDATAEXECOUT pCallbackData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvData);
- AssertPtr(pCallbackData);
- AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbData, VERR_INVALID_PARAMETER);
- AssertReturn(CALLBACKDATAMAGIC_EXEC_OUT == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
-
- vrc = onProcessOutput(pCallback, pCallbackData);
+ vrc = onProcessOutput(pCbCtx, pSvcCb);
break;
}
- case GUEST_EXEC_SEND_INPUT_STATUS:
+ case GUEST_EXEC_INPUT_STATUS:
{
- PCALLBACKDATAEXECINSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECINSTATUS>(pvData);
- AssertPtr(pCallbackData);
- AssertReturn(sizeof(CALLBACKDATAEXECINSTATUS) == cbData, VERR_INVALID_PARAMETER);
- AssertReturn(CALLBACKDATAMAGIC_EXEC_IN_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
-
- vrc = onProcessInputStatus(pCallback, pCallbackData);
+ vrc = onProcessInputStatus(pCbCtx, pSvcCb);
break;
}
default:
/* Silently ignore not implemented functions. */
- vrc = VERR_NOT_IMPLEMENTED;
+ vrc = VERR_NOT_SUPPORTED;
break;
}
@@ -488,67 +509,38 @@ int GuestProcess::callbackDispatcher(uint32_t uContextID, uint32_t uFunction, vo
return vrc;
}
-inline bool GuestProcess::callbackExists(uint32_t uContextID)
-{
- GuestCtrlCallbacks::const_iterator it =
- mData.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
- return (it == mData.mCallbacks.end()) ? false : true;
-}
-
-inline int GuestProcess::callbackRemove(uint32_t uContextID)
-{
- LogFlowThisFunc(("Removing callback (Session: %RU32, Process: %RU32, Count=%RU32) CID=%RU32\n",
- VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID),
- VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(uContextID),
- VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID),
- uContextID));
-
- GuestCtrlCallbacks::iterator it =
- mData.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
- if (it != mData.mCallbacks.end())
- {
- delete it->second;
- mData.mCallbacks.erase(it);
-
- return VINF_SUCCESS;
- }
-
- return VERR_NOT_FOUND;
-}
-
/**
* Checks if the current assigned PID matches another PID (from a callback).
*
* In protocol v1 we don't have the possibility to terminate/kill
- * processes so it can happen that a formerly start process A
+ * processes so it can happen that a formerly started process A
* (which has the context ID 0 (session=0, process=0, count=0) will
* send a delayed message to the host if this process has already
* been discarded there and the same context ID was reused by
* a process B. Process B in turn then has a different guest PID.
*
+ * Note: This also can happen when restoring from a saved state which
+ * had a guest process running.
+ *
* @return IPRT status code.
* @param uPID PID to check.
*/
inline int GuestProcess::checkPID(uint32_t uPID)
{
+ int rc = VINF_SUCCESS;
+
/* Was there a PID assigned yet? */
if (mData.mPID)
{
- /*
-
- */
- if (mData.mParent->getProtocolVersion() < 2)
+ if (RT_UNLIKELY(mData.mPID != uPID))
{
- /* Simply ignore the stale requests. */
- return (mData.mPID == uPID)
- ? VINF_SUCCESS : VERR_NOT_FOUND;
+ LogFlowFunc(("Stale guest process (PID=%RU32) sent data to a newly started process (pProcesS=%p, PID=%RU32, status=%RU32)\n",
+ uPID, this, mData.mPID, mData.mStatus));
+ rc = VERR_NOT_FOUND;
}
- /* This should never happen! */
- AssertReleaseMsg(mData.mPID == uPID, ("Unterminated guest process (PID %RU32) sent data to a newly started process (PID %RU32)\n",
- uPID, mData.mPID));
}
- return VINF_SUCCESS;
+ return rc;
}
/* static */
@@ -600,7 +592,7 @@ Utf8Str GuestProcess::guestErrorToString(int guestRc)
break;
case VERR_MAX_PROCS_REACHED:
- strError += Utf8StrFmt(tr("Maximum number of parallel guest processes has been 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. */
@@ -612,7 +604,7 @@ Utf8Str GuestProcess::guestErrorToString(int guestRc)
break;
default:
- strError += Utf8StrFmt(tr("%Rrc"), guestRc);
+ strError += Utf8StrFmt("%Rrc", guestRc);
break;
}
@@ -626,226 +618,205 @@ inline bool GuestProcess::isAlive(void)
|| mData.mStatus == ProcessStatus_Terminating);
}
-bool GuestProcess::isReady(void)
+inline bool GuestProcess::hasEnded(void)
{
- AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
- if (mData.mStatus == ProcessStatus_Started)
- {
- Assert(mData.mPID); /* PID must not be 0. */
- return true;
- }
-
- return false;
+ return ( mData.mStatus == ProcessStatus_TerminatedNormally
+ || mData.mStatus == ProcessStatus_TerminatedSignal
+ || mData.mStatus == ProcessStatus_TerminatedAbnormally
+ || mData.mStatus == ProcessStatus_TimedOutKilled
+ || mData.mStatus == ProcessStatus_TimedOutAbnormally
+ || mData.mStatus == ProcessStatus_Down
+ || mData.mStatus == ProcessStatus_Error);
}
-int GuestProcess::onGuestDisconnected(GuestCtrlCallback *pCallback, PCALLBACKDATACLIENTDISCONNECTED pData)
+int GuestProcess::onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
{
- /* pCallback is optional. */
- AssertPtrReturn(pData, VERR_INVALID_POINTER);
-
- LogFlowThisFunc(("uPID=%RU32, pCallback=%p, pData=%p\n", mData.mPID, pCallback, pData));
-
- mData.mStatus = ProcessStatus_Down;
-
- /* First, signal callback in every case. */
- if (pCallback)
- pCallback->Signal();
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
- /* Do we need to report a termination? */
- ProcessWaitResult_T waitRes;
- if (mData.mProcess.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
- waitRes = ProcessWaitResult_Status; /* No, just report a status. */
- else
- waitRes = ProcessWaitResult_Terminate;
-
- /* Signal in any case. */
- int vrc = signalWaiters(waitRes);
- AssertRC(vrc);
+ int vrc = setProcessStatus(ProcessStatus_Down, VINF_SUCCESS);
LogFlowFuncLeaveRC(vrc);
return vrc;
}
-int GuestProcess::onProcessInputStatus(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECINSTATUS pData)
+int GuestProcess::onProcessInputStatus(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
{
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
/* pCallback is optional. */
- AssertPtrReturn(pData, VERR_INVALID_POINTER);
- LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32, cbProcessed=%RU32, pCallback=%p, pData=%p\n",
- mData.mPID, pData->u32Status, pData->u32Flags, pData->cbProcessed, pCallback, pData));
-
- int vrc = checkPID(pData->u32PID);
- if (RT_FAILURE(vrc))
- return vrc;
+ if (pSvcCbData->mParms < 5)
+ return VERR_INVALID_PARAMETER;
- /* First, signal callback in every case (if available). */
- if (pCallback)
+ CALLBACKDATA_PROC_INPUT dataCb;
+ /* pSvcCb->mpaParms[0] always contains the context ID. */
+ int vrc = pSvcCbData->mpaParms[1].getUInt32(&dataCb.uPID);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCbData->mpaParms[2].getUInt32(&dataCb.uStatus);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCbData->mpaParms[3].getUInt32(&dataCb.uFlags);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCbData->mpaParms[4].getUInt32(&dataCb.uProcessed);
+ AssertRCReturn(vrc, vrc);
+
+ LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RI32, cbProcessed=%RU32\n",
+ dataCb.uPID, dataCb.uStatus, dataCb.uFlags, dataCb.uProcessed));
+
+ vrc = checkPID(dataCb.uPID);
+ if (RT_SUCCESS(vrc))
{
- vrc = pCallback->SetData(pData, sizeof(CALLBACKDATAEXECINSTATUS));
+ ProcessInputStatus_T inputStatus = ProcessInputStatus_Undefined;
+ switch (dataCb.uStatus)
+ {
+ case INPUT_STS_WRITTEN:
+ inputStatus = ProcessInputStatus_Written;
+ break;
+ case INPUT_STS_ERROR:
+ inputStatus = ProcessInputStatus_Broken;
+ break;
+ case INPUT_STS_TERMINATED:
+ inputStatus = ProcessInputStatus_Broken;
+ break;
+ case INPUT_STS_OVERFLOW:
+ inputStatus = ProcessInputStatus_Overflow;
+ break;
+ case INPUT_STS_UNDEFINED:
+ /* Fall through is intentional. */
+ default:
+ AssertMsg(!dataCb.uProcessed, ("Processed data is not 0 in undefined input state\n"));
+ break;
+ }
- int rc2 = pCallback->Signal();
- if (RT_SUCCESS(vrc))
- vrc = rc2;
- }
+ if (inputStatus != ProcessInputStatus_Undefined)
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- /* Then do the WaitFor signalling stuff. */
- uint32_t uWaitFlags = mData.mWaitEvent
- ? mData.mWaitEvent->GetWaitFlags() : 0;
- if (uWaitFlags & ProcessWaitForFlag_StdIn)
- {
- int rc2 = signalWaiters(ProcessWaitResult_StdIn);
- if (RT_SUCCESS(vrc))
- vrc = rc2;
+ /* Copy over necessary data before releasing lock again. */
+ uint32_t uPID = mData.mPID;
+ /** @todo Also handle mSession? */
+
+ alock.release(); /* Release lock before firing off event. */
+
+ fireGuestProcessInputNotifyEvent(mEventSource, mSession, this,
+ uPID, 0 /* StdIn */, dataCb.uProcessed, inputStatus);
+ }
}
LogFlowFuncLeaveRC(vrc);
return vrc;
}
-int GuestProcess::onProcessNotifyIO(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData)
+int GuestProcess::onProcessNotifyIO(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
{
- /* pCallback is optional. */
- AssertPtrReturn(pData, VERR_INVALID_POINTER);
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
- return 0;
+ return VERR_NOT_IMPLEMENTED;
}
-int GuestProcess::onProcessStatusChange(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData)
+int GuestProcess::onProcessStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
{
- /* pCallback is optional. */
- AssertPtrReturn(pData, VERR_INVALID_POINTER);
+ AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
- LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32, pCallback=%p, pData=%p\n",
- pData->u32PID, pData->u32Status, pData->u32Flags, pCallback, pData));
-
- int vrc = checkPID(pData->u32PID);
- if (RT_FAILURE(vrc))
- return vrc;
-
- ProcessStatus_T procStatus = ProcessStatus_Undefined;
- int procRc = VINF_SUCCESS;
-
- bool fSignalWaiters = false;
- ProcessWaitResult_T waitRes;
+ if (pSvcCbData->mParms < 5)
+ return VERR_INVALID_PARAMETER;
- uint32_t uWaitFlags = mData.mWaitEvent
- ? mData.mWaitEvent->GetWaitFlags() : 0;
- switch (pData->u32Status)
+ CALLBACKDATA_PROC_STATUS dataCb;
+ /* pSvcCb->mpaParms[0] always contains the context ID. */
+ int vrc = pSvcCbData->mpaParms[1].getUInt32(&dataCb.uPID);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCbData->mpaParms[2].getUInt32(&dataCb.uStatus);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCbData->mpaParms[3].getUInt32(&dataCb.uFlags);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCbData->mpaParms[4].getPointer(&dataCb.pvData, &dataCb.cbData);
+ AssertRCReturn(vrc, vrc);
+
+ LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32\n",
+ dataCb.uPID, dataCb.uStatus, dataCb.uFlags));
+
+ vrc = checkPID(dataCb.uPID);
+ if (RT_SUCCESS(vrc))
{
- case PROC_STS_STARTED:
- {
- fSignalWaiters = (uWaitFlags & ProcessWaitForFlag_Start);
- /* If the caller only wants to wait until the process has been started,
- * notify in any case. */
- if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
- fSignalWaiters = true;
- waitRes = ProcessWaitResult_Start;
-
- procStatus = ProcessStatus_Started;
- mData.mPID = pData->u32PID; /* Set the process PID. */
- break;
- }
-
- case PROC_STS_TEN:
- {
- fSignalWaiters = true; /* Signal in any case. */
- waitRes = ProcessWaitResult_Terminate;
-
- procStatus = ProcessStatus_TerminatedNormally;
- mData.mExitCode = pData->u32Flags; /* Contains the exit code. */
- break;
- }
-
- case PROC_STS_TES:
- {
- fSignalWaiters = true; /* Signal in any case. */
- waitRes = ProcessWaitResult_Terminate;
-
- procStatus = ProcessStatus_TerminatedSignal;
- mData.mExitCode = pData->u32Flags; /* Contains the signal. */
- break;
- }
+ ProcessStatus_T procStatus = ProcessStatus_Undefined;
+ int procRc = VINF_SUCCESS;
- case PROC_STS_TEA:
+ switch (dataCb.uStatus)
{
- fSignalWaiters = true; /* Signal in any case. */
- waitRes = ProcessWaitResult_Terminate;
+ case PROC_STS_STARTED:
+ {
+ procStatus = ProcessStatus_Started;
- procStatus = ProcessStatus_TerminatedAbnormally;
- break;
- }
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ mData.mPID = dataCb.uPID; /* Set the process PID. */
+ break;
+ }
- case PROC_STS_TOK:
- {
- fSignalWaiters = true; /* Signal in any case. */
- waitRes = ProcessWaitResult_Timeout;
+ case PROC_STS_TEN:
+ {
+ procStatus = ProcessStatus_TerminatedNormally;
- procStatus = ProcessStatus_TimedOutKilled;
- break;
- }
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ mData.mExitCode = dataCb.uFlags; /* Contains the exit code. */
+ break;
+ }
- case PROC_STS_TOA:
- {
- fSignalWaiters = true; /* Signal in any case. */
- waitRes = ProcessWaitResult_Timeout;
+ case PROC_STS_TES:
+ {
+ procStatus = ProcessStatus_TerminatedSignal;
- procStatus = ProcessStatus_TimedOutAbnormally;
- break;
- }
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ mData.mExitCode = dataCb.uFlags; /* Contains the signal. */
+ break;
+ }
- case PROC_STS_DWN:
- {
- fSignalWaiters = true; /* Signal in any case. */
- /* Do we need to report termination? */
- if (mData.mProcess.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
- waitRes = ProcessWaitResult_Status;
- else
- waitRes = ProcessWaitResult_Terminate;
+ case PROC_STS_TEA:
+ {
+ procStatus = ProcessStatus_TerminatedAbnormally;
+ break;
+ }
- procStatus = ProcessStatus_Down;
- break;
- }
+ case PROC_STS_TOK:
+ {
+ procStatus = ProcessStatus_TimedOutKilled;
+ break;
+ }
- case PROC_STS_ERROR:
- {
- fSignalWaiters = true; /* Signal in any case. */
- waitRes = ProcessWaitResult_Error;
+ case PROC_STS_TOA:
+ {
+ procStatus = ProcessStatus_TimedOutAbnormally;
+ break;
+ }
- procRc = pData->u32Flags; /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
- procStatus = ProcessStatus_Error;
- break;
- }
+ case PROC_STS_DWN:
+ {
+ procStatus = ProcessStatus_Down;
+ break;
+ }
- case PROC_STS_UNDEFINED:
- default:
- {
- /* Silently skip this request. */
- fSignalWaiters = true; /* Signal in any case. */
- waitRes = ProcessWaitResult_Status;
+ case PROC_STS_ERROR:
+ {
+ procRc = dataCb.uFlags; /* mFlags contains the IPRT error sent from the guest. */
+ procStatus = ProcessStatus_Error;
+ break;
+ }
- procStatus = ProcessStatus_Undefined;
- break;
+ case PROC_STS_UNDEFINED:
+ default:
+ {
+ /* Silently skip this request. */
+ procStatus = ProcessStatus_Undefined;
+ break;
+ }
}
- }
-
- LogFlowThisFunc(("Got rc=%Rrc, waitRes=%d, procSts=%ld, procRc=%Rrc, fSignalWaiters=%RTbool\n",
- vrc, waitRes, procStatus, procRc, fSignalWaiters));
- /* Set the process status. */
- int rc2 = setProcessStatus(procStatus, procRc);
- if (RT_SUCCESS(vrc))
- vrc = rc2;
+ LogFlowThisFunc(("Got rc=%Rrc, procSts=%RU32, procRc=%Rrc\n",
+ vrc, procStatus, procRc));
- /*
- * Now do the signalling stuff.
- */
- if (pCallback)
- vrc = pCallback->Signal(procRc);
-
- if (fSignalWaiters)
- {
- rc2 = signalWaiters(waitRes, procRc);
+ /* Set the process status. */
+ int rc2 = setProcessStatus(procStatus, procRc);
if (RT_SUCCESS(vrc))
vrc = rc2;
}
@@ -854,58 +825,64 @@ int GuestProcess::onProcessStatusChange(GuestCtrlCallback *pCallback, PCALLBACKD
return vrc;
}
-int GuestProcess::onProcessOutput(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECOUT pData)
+int GuestProcess::onProcessOutput(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
{
- /* pCallback is optional. */
- AssertPtrReturn(pData, VERR_INVALID_POINTER);
-
- LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, pCallback=%p, pData=%p\n",
- mData.mPID, pData->u32HandleId, pData->u32Flags, pData->pvData, pData->cbData, pCallback, pData));
+ AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
- int vrc = checkPID(pData->u32PID);
- if (RT_FAILURE(vrc))
- return vrc;
+ if (pSvcCbData->mParms < 5)
+ return VERR_INVALID_PARAMETER;
- /* First, signal callback in every case (if available). */
- if (pCallback)
+ CALLBACKDATA_PROC_OUTPUT dataCb;
+ /* pSvcCb->mpaParms[0] always contains the context ID. */
+ int vrc = pSvcCbData->mpaParms[1].getUInt32(&dataCb.uPID);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCbData->mpaParms[2].getUInt32(&dataCb.uHandle);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCbData->mpaParms[3].getUInt32(&dataCb.uFlags);
+ AssertRCReturn(vrc, vrc);
+ vrc = pSvcCbData->mpaParms[4].getPointer(&dataCb.pvData, &dataCb.cbData);
+ AssertRCReturn(vrc, vrc);
+
+ LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RI32, pvData=%p, cbData=%RU32\n",
+ dataCb.uPID, dataCb.uHandle, dataCb.uFlags, dataCb.pvData, dataCb.cbData));
+
+ vrc = checkPID(dataCb.uPID);
+ if (RT_SUCCESS(vrc))
{
- vrc = pCallback->SetData(pData, sizeof(CALLBACKDATAEXECOUT));
+ com::SafeArray<BYTE> data((size_t)dataCb.cbData);
+ if (dataCb.cbData)
+ data.initFrom((BYTE*)dataCb.pvData, dataCb.cbData);
- int rc2 = pCallback->Signal();
- if (RT_SUCCESS(vrc))
- vrc = rc2;
+ fireGuestProcessOutputEvent(mEventSource, mSession, this,
+ mData.mPID, dataCb.uHandle, dataCb.cbData, ComSafeArrayAsInParam(data));
}
- /* Then do the WaitFor signalling stuff. */
- BOOL fSignal = FALSE;
- uint32_t uWaitFlags = mData.mWaitEvent
- ? mData.mWaitEvent->GetWaitFlags() : 0;
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
- if ( (uWaitFlags & ProcessWaitForFlag_StdOut)
- || (uWaitFlags & ProcessWaitForFlag_StdErr))
- {
- fSignal = TRUE;
- }
- else if ( (uWaitFlags & ProcessWaitForFlag_StdOut)
- && (pData->u32HandleId == OUTPUT_HANDLE_ID_STDOUT))
- {
- fSignal = TRUE;
- }
- else if ( (uWaitFlags & ProcessWaitForFlag_StdErr)
- && (pData->u32HandleId == OUTPUT_HANDLE_ID_STDERR))
- {
- fSignal = TRUE;
- }
+/**
+ * Called by IGuestSession right before this process gets
+ * removed from the public process list.
+ */
+int GuestProcess::onRemove(void)
+{
+ LogFlowThisFuncEnter();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ int vrc = VINF_SUCCESS;
- if (fSignal)
+ /*
+ * Note: The event source stuff holds references to this object,
+ * so make sure that this is cleaned up *before* calling uninit().
+ */
+ if (!mEventSource.isNull())
{
- int rc2;
- if (pData->u32HandleId == OUTPUT_HANDLE_ID_STDOUT)
- rc2 = signalWaiters(ProcessWaitResult_StdOut);
- else
- rc2 = signalWaiters(ProcessWaitResult_StdErr);
- if (RT_SUCCESS(vrc))
- vrc = rc2;
+ mEventSource->UnregisterListener(mLocalListener);
+
+ mLocalListener.setNull();
+ unconst(mEventSource).setNull();
}
LogFlowFuncLeaveRC(vrc);
@@ -913,7 +890,7 @@ int GuestProcess::onProcessOutput(GuestCtrlCallback *pCallback, PCALLBACKDATAEXE
}
int GuestProcess::readData(uint32_t uHandle, uint32_t uSize, uint32_t uTimeoutMS,
- void *pvData, size_t cbData, size_t *pcbRead, int *pGuestRc)
+ void *pvData, size_t cbData, uint32_t *pcbRead, int *pGuestRc)
{
LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%RU32, pGuestRc=%p\n",
mData.mPID, uHandle, uSize, uTimeoutMS, pvData, cbData, pGuestRc));
@@ -924,7 +901,15 @@ int GuestProcess::readData(uint32_t uHandle, uint32_t uSize, uint32_t uTimeoutMS
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- if (mData.mStatus != ProcessStatus_Started)
+ if ( mData.mStatus != ProcessStatus_Started
+ /* Skip reading if the process wasn't started with the appropriate
+ * flags. */
+ || ( ( uHandle == OUTPUT_HANDLE_ID_STDOUT
+ || uHandle == OUTPUT_HANDLE_ID_STDOUT_DEPRECATED)
+ && !(mData.mProcess.mFlags & ProcessCreateFlag_WaitForStdOut))
+ || ( uHandle == OUTPUT_HANDLE_ID_STDERR
+ && !(mData.mProcess.mFlags & ProcessCreateFlag_WaitForStdErr))
+ )
{
if (pcbRead)
*pcbRead = 0;
@@ -933,138 +918,120 @@ int GuestProcess::readData(uint32_t uHandle, uint32_t uSize, uint32_t uTimeoutMS
return VINF_SUCCESS; /* Nothing to read anymore. */
}
- int vrc = VINF_SUCCESS;
+ int vrc;
- GuestCtrlCallback *pCallbackRead = NULL;
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
try
{
- pCallbackRead = new GuestCtrlCallback();
+ /*
+ * On Guest Additions < 4.3 there is no guarantee that the process status
+ * change arrives *after* the output event, e.g. if this was the last output
+ * block being read and the process will report status "terminate".
+ * So just skip checking for process status change and only wait for the
+ * output event.
+ */
+ if (mSession->getProtocolVersion() >= 2)
+ eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
+ eventTypes.push_back(VBoxEventType_OnGuestProcessOutput);
+
+ vrc = registerWaitEvent(eventTypes, &pEvent);
}
- catch(std::bad_alloc &)
+ catch (std::bad_alloc)
{
vrc = VERR_NO_MEMORY;
}
- /* Create callback and add it to the map. */
- uint32_t uContextID = 0;
- if (RT_SUCCESS(vrc))
- {
- vrc = pCallbackRead->Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT);
- if (RT_SUCCESS(vrc))
- vrc = callbackAdd(pCallbackRead, &uContextID);
- }
-
- alock.release(); /* Drop the write lock again. */
+ if (RT_FAILURE(vrc))
+ return vrc;
if (RT_SUCCESS(vrc))
{
- VBOXHGCMSVCPARM paParms[5];
-
+ VBOXHGCMSVCPARM paParms[8];
int i = 0;
- paParms[i++].setUInt32(uContextID);
+ paParms[i++].setUInt32(pEvent->ContextID());
paParms[i++].setUInt32(mData.mPID);
paParms[i++].setUInt32(uHandle);
paParms[i++].setUInt32(0 /* Flags, none set yet. */);
+ alock.release(); /* Drop the write lock before sending. */
+
vrc = sendCommand(HOST_EXEC_GET_OUTPUT, i, paParms);
}
if (RT_SUCCESS(vrc))
- {
- /*
- * Let's wait for the process being started.
- * Note: Be sure not keeping a AutoRead/WriteLock here.
- */
- LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
- vrc = pCallbackRead->Wait(uTimeoutMS);
- if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
- {
- int guestRc = pCallbackRead->GetResultCode();
- LogFlowThisFunc(("Callback returned rc=%Rrc, cbData=%RU32\n", guestRc, pCallbackRead->GetDataSize()));
-
- if (RT_SUCCESS(guestRc))
- {
- Assert(pCallbackRead->GetDataSize() == sizeof(CALLBACKDATAEXECOUT));
- PCALLBACKDATAEXECOUT pData = (PCALLBACKDATAEXECOUT)pCallbackRead->GetDataRaw();
- AssertPtr(pData);
-
- size_t cbRead = pData->cbData;
- if (cbRead)
- {
- Assert(cbData >= cbRead);
- memcpy(pvData, pData->pvData, cbRead);
- }
-
- LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
-
- if (pcbRead)
- *pcbRead = cbRead;
- }
- else
- vrc = VERR_GENERAL_FAILURE; /** @todo Special guest control rc needed! */
-
- if (pGuestRc)
- *pGuestRc = guestRc;
- }
- }
-
- alock.acquire();
+ vrc = waitForOutput(pEvent, uHandle, uTimeoutMS,
+ pvData, cbData, pcbRead);
- AssertPtr(pCallbackRead);
- int rc2 = callbackRemove(uContextID);
- if (RT_SUCCESS(vrc))
- vrc = rc2;
+ unregisterWaitEvent(pEvent);
LogFlowFuncLeaveRC(vrc);
return vrc;
}
-int GuestProcess::sendCommand(uint32_t uFunction,
- uint32_t uParms, PVBOXHGCMSVCPARM paParms)
+/* Does not do locking; caller is responsible for that! */
+int GuestProcess::setProcessStatus(ProcessStatus_T procStatus, int procRc)
{
LogFlowThisFuncEnter();
- ComObjPtr<Console> pConsole = mData.mConsole;
- 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("VBoxGuestControlSvc", uFunction, uParms, paParms);
- if (RT_FAILURE(vrc))
- {
- int rc2 = setProcessStatus(ProcessStatus_Error, vrc);
- AssertRC(rc2);
- }
-
- LogFlowFuncLeaveRC(vrc);
- return vrc;
-}
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-/* Does not do locking; caller is responsible for that! */
-int GuestProcess::setProcessStatus(ProcessStatus_T procStatus, int procRc)
-{
- LogFlowThisFunc(("oldStatus=%ld, newStatus=%ld, procRc=%Rrc\n",
+ LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, procRc=%Rrc\n",
mData.mStatus, procStatus, procRc));
-#ifdef DEBUG
if (procStatus == ProcessStatus_Error)
{
AssertMsg(RT_FAILURE(procRc), ("Guest rc must be an error (%Rrc)\n", procRc));
/* 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));
+ AssertMsg(RT_SUCCESS(mData.mLastError), ("Guest rc already set (to %Rrc)\n", mData.mLastError));
}
else
AssertMsg(RT_SUCCESS(procRc), ("Guest rc must not be an error (%Rrc)\n", procRc));
-#endif
- mData.mStatus = procStatus;
- mData.mRC = procRc;
+ int rc = VINF_SUCCESS;
- return VINF_SUCCESS;
+ if (mData.mStatus != procStatus) /* Was there a process status change? */
+ {
+ mData.mStatus = procStatus;
+ mData.mLastError = procRc;
+
+ ComObjPtr<VirtualBoxErrorInfo> errorInfo;
+ HRESULT hr = errorInfo.createObject();
+ ComAssertComRC(hr);
+ if (RT_FAILURE(mData.mLastError))
+ {
+ hr = errorInfo->initEx(VBOX_E_IPRT_ERROR, mData.mLastError,
+ COM_IIDOF(IGuestProcess), getComponentName(),
+ guestErrorToString(mData.mLastError));
+ ComAssertComRC(hr);
+ }
+
+ /* Copy over necessary data before releasing lock again. */
+ uint32_t uPID = mData.mPID;
+ /** @todo Also handle mSession? */
+
+ alock.release(); /* Release lock before firing off event. */
+
+ fireGuestProcessStateChangedEvent(mEventSource, mSession, this,
+ uPID, procStatus, errorInfo);
+#if 0
+ /*
+ * On Guest Additions < 4.3 there is no guarantee that outstanding
+ * requests will be delivered to the host after the process has ended,
+ * so just cancel all waiting events here to not let clients run
+ * into timeouts.
+ */
+ if ( mSession->getProtocolVersion() < 2
+ && hasEnded())
+ {
+ LogFlowThisFunc(("Process ended, canceling outstanding wait events ...\n"));
+ rc = cancelWaitEvents();
+ }
+#endif
+ }
+
+ return rc;
}
/* static */
@@ -1076,170 +1043,147 @@ HRESULT GuestProcess::setErrorExternal(VirtualBoxBase *pInterface, int guestRc)
return pInterface->setError(VBOX_E_IPRT_ERROR, GuestProcess::guestErrorToString(guestRc).c_str());
}
-int GuestProcess::signalWaiters(ProcessWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
+int GuestProcess::startProcess(uint32_t uTimeoutMS, int *pGuestRc)
{
- 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 GuestProcess::startProcess(int *pGuestRc)
-{
- LogFlowThisFunc(("aCmd=%s, aTimeoutMS=%RU32, fFlags=%x\n",
- mData.mProcess.mCommand.c_str(), mData.mProcess.mTimeoutMS, mData.mProcess.mFlags));
+ LogFlowThisFunc(("uTimeoutMS=%RU32, procCmd=%s, procTimeoutMS=%RU32, procFlags=%x, sessionID=%RU32\n",
+ uTimeoutMS, mData.mProcess.mCommand.c_str(), mData.mProcess.mTimeoutMS, mData.mProcess.mFlags,
+ mSession->getId()));
/* Wait until the caller function (if kicked off by a thread)
* has returned and continue operation. */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- int vrc = VINF_SUCCESS;
- uint32_t uContextID = 0;
+ mData.mStatus = ProcessStatus_Starting;
- GuestCtrlCallback *pCallbackStart;
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
try
{
- pCallbackStart = new GuestCtrlCallback();
+ eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
+
+ vrc = registerWaitEvent(eventTypes, &pEvent);
}
- catch(std::bad_alloc &)
+ catch (std::bad_alloc)
{
vrc = VERR_NO_MEMORY;
}
- if (RT_SUCCESS(vrc))
- {
- mData.mStatus = ProcessStatus_Starting;
+ if (RT_FAILURE(vrc))
+ return vrc;
- /* Create callback and add it to the map. */
- vrc = pCallbackStart->Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START);
- if (RT_SUCCESS(vrc))
- vrc = callbackAdd(pCallbackStart, &uContextID);
- }
+ GuestSession *pSession = mSession;
+ AssertPtr(pSession);
- if (RT_SUCCESS(vrc))
- {
- GuestSession *pSession = mData.mParent;
- AssertPtr(pSession);
+ const GuestCredentials &sessionCreds = pSession->getCredentials();
- const GuestCredentials &sessionCreds = pSession->getCredentials();
+ /* Prepare arguments. */
+ char *pszArgs = NULL;
+ size_t cArgs = mData.mProcess.mArguments.size();
+ if (cArgs >= UINT32_MAX)
+ vrc = VERR_BUFFER_OVERFLOW;
- /* Prepare arguments. */
- char *pszArgs = NULL;
- size_t cArgs = mData.mProcess.mArguments.size();
- if (cArgs >= UINT32_MAX)
- vrc = VERR_BUFFER_OVERFLOW;
+ if ( RT_SUCCESS(vrc)
+ && cArgs)
+ {
+ char **papszArgv = (char**)RTMemAlloc((cArgs + 1) * sizeof(char*));
+ AssertReturn(papszArgv, VERR_NO_MEMORY);
- if ( RT_SUCCESS(vrc)
- && cArgs)
+ for (size_t i = 0; i < cArgs && RT_SUCCESS(vrc); i++)
{
- char **papszArgv = (char**)RTMemAlloc((cArgs + 1) * sizeof(char*));
- AssertReturn(papszArgv, VERR_NO_MEMORY);
-
- for (size_t i = 0; i < cArgs && RT_SUCCESS(vrc); i++)
- {
- const char *pszCurArg = mData.mProcess.mArguments[i].c_str();
- AssertPtr(pszCurArg);
- vrc = RTStrDupEx(&papszArgv[i], pszCurArg);
- }
- papszArgv[cArgs] = NULL;
+ const char *pszCurArg = mData.mProcess.mArguments[i].c_str();
+ AssertPtr(pszCurArg);
+ vrc = RTStrDupEx(&papszArgv[i], pszCurArg);
+ }
+ papszArgv[cArgs] = NULL;
- if (RT_SUCCESS(vrc))
- vrc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
+ if (RT_SUCCESS(vrc))
+ vrc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
- if (papszArgv)
- {
- size_t i = 0;
- while (papszArgv[i])
- RTStrFree(papszArgv[i++]);
- RTMemFree(papszArgv);
- }
+ if (papszArgv)
+ {
+ size_t i = 0;
+ while (papszArgv[i])
+ RTStrFree(papszArgv[i++]);
+ RTMemFree(papszArgv);
}
+ }
- /* Calculate arguments size (in bytes). */
- size_t cbArgs = 0;
- if (RT_SUCCESS(vrc))
- cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
+ /* Calculate arguments size (in bytes). */
+ size_t cbArgs = 0;
+ if (RT_SUCCESS(vrc))
+ cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
- /* Prepare environment. */
- void *pvEnv = NULL;
- size_t cbEnv = 0;
- if (RT_SUCCESS(vrc))
- vrc = mData.mProcess.mEnvironment.BuildEnvironmentBlock(&pvEnv, &cbEnv, NULL /* cEnv */);
+ /* Prepare environment. */
+ void *pvEnv = NULL;
+ size_t cbEnv = 0;
+ if (RT_SUCCESS(vrc))
+ vrc = mData.mProcess.mEnvironment.BuildEnvironmentBlock(&pvEnv, &cbEnv, NULL /* cEnv */);
- if (RT_SUCCESS(vrc))
+ if (RT_SUCCESS(vrc))
+ {
+ AssertPtr(mSession);
+ uint32_t uProtocol = mSession->getProtocolVersion();
+
+ /* Prepare HGCM call. */
+ VBOXHGCMSVCPARM paParms[16];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setPointer((void*)mData.mProcess.mCommand.c_str(),
+ (ULONG)mData.mProcess.mCommand.length() + 1);
+ paParms[i++].setUInt32(mData.mProcess.mFlags);
+ paParms[i++].setUInt32((uint32_t)mData.mProcess.mArguments.size());
+ paParms[i++].setPointer((void*)pszArgs, (uint32_t)cbArgs);
+ paParms[i++].setUInt32((uint32_t)mData.mProcess.mEnvironment.Size());
+ paParms[i++].setUInt32((uint32_t)cbEnv);
+ paParms[i++].setPointer((void*)pvEnv, (uint32_t)cbEnv);
+ if (uProtocol < 2)
{
- /* Prepare HGCM call. */
- VBOXHGCMSVCPARM paParms[15];
- int i = 0;
- paParms[i++].setUInt32(uContextID);
- paParms[i++].setPointer((void*)mData.mProcess.mCommand.c_str(),
- (ULONG)mData.mProcess.mCommand.length() + 1);
- paParms[i++].setUInt32(mData.mProcess.mFlags);
- paParms[i++].setUInt32(mData.mProcess.mArguments.size());
- paParms[i++].setPointer((void*)pszArgs, cbArgs);
- paParms[i++].setUInt32(mData.mProcess.mEnvironment.Size());
- paParms[i++].setUInt32(cbEnv);
- paParms[i++].setPointer((void*)pvEnv, cbEnv);
+ /* In protocol v1 (VBox < 4.3) the credentials were part of the execution
+ * call. In newer protocols these credentials are part of the opened guest
+ * session, so not needed anymore here. */
paParms[i++].setPointer((void*)sessionCreds.mUser.c_str(), (ULONG)sessionCreds.mUser.length() + 1);
paParms[i++].setPointer((void*)sessionCreds.mPassword.c_str(), (ULONG)sessionCreds.mPassword.length() + 1);
- /** @todo New command needs the domain as well! */
-
- /*
- * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
- * until the process was started - the process itself then gets an infinite timeout for execution.
- * This is handy when we want to start a process inside a worker thread within a certain timeout
- * but let the started process perform lengthly operations then.
- */
- if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
- paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
- else
- paParms[i++].setUInt32(mData.mProcess.mTimeoutMS);
-
- /* Note: Don't hold the write lock in here, because setErrorInternal */
- vrc = sendCommand(HOST_EXEC_CMD, i, paParms);
+ }
+ /*
+ * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
+ * until the process was started - the process itself then gets an infinite timeout for execution.
+ * This is handy when we want to start a process inside a worker thread within a certain timeout
+ * but let the started process perform lengthly operations then.
+ */
+ if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
+ paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
+ else
+ paParms[i++].setUInt32(mData.mProcess.mTimeoutMS);
+ if (uProtocol >= 2)
+ {
+ paParms[i++].setUInt32(mData.mProcess.mPriority);
+ /* CPU affinity: We only support one CPU affinity block at the moment,
+ * so that makes up to 64 CPUs total. This can be more in the future. */
+ paParms[i++].setUInt32(1);
+ /* The actual CPU affinity blocks. */
+ paParms[i++].setPointer((void*)&mData.mProcess.mAffinity, sizeof(mData.mProcess.mAffinity));
}
- GuestEnvironment::FreeEnvironmentBlock(pvEnv);
- if (pszArgs)
- RTStrFree(pszArgs);
-
- uint32_t uTimeoutMS = mData.mProcess.mTimeoutMS;
-
- /* Drop the write lock again before waiting. */
- alock.release();
+ alock.release(); /* Drop the write lock before sending. */
- if (RT_SUCCESS(vrc))
+ vrc = sendCommand(HOST_EXEC_CMD, i, paParms);
+ if (RT_FAILURE(vrc))
{
- /*
- * Let's wait for the process being started.
- * Note: Be sure not keeping a AutoRead/WriteLock here.
- */
- LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
- vrc = pCallbackStart->Wait(uTimeoutMS);
- if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
- {
- int guestRc = pCallbackStart->GetResultCode();
- if (pGuestRc)
- *pGuestRc = guestRc;
- LogFlowThisFunc(("Callback returned rc=%Rrc\n", guestRc));
- }
- else
- vrc = VERR_TIMEOUT;
+ int rc2 = setProcessStatus(ProcessStatus_Error, vrc);
+ AssertRC(rc2);
}
+ }
- AutoWriteLock awlock(this COMMA_LOCKVAL_SRC_POS);
+ GuestEnvironment::FreeEnvironmentBlock(pvEnv);
+ if (pszArgs)
+ RTStrFree(pszArgs);
- AssertPtr(pCallbackStart);
- int rc2 = callbackRemove(uContextID);
- if (RT_SUCCESS(vrc))
- vrc = rc2;
- }
+ if (RT_SUCCESS(vrc))
+ vrc = waitForStatusChange(pEvent, uTimeoutMS,
+ NULL /* Process status */, pGuestRc);
+ unregisterWaitEvent(pEvent);
LogFlowFuncLeaveRC(vrc);
return vrc;
@@ -1291,208 +1235,477 @@ DECLCALLBACK(int) GuestProcess::startProcessThread(RTTHREAD Thread, void *pvUser
AutoCaller autoCaller(pProcess);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- int vrc = pProcess->startProcess(NULL /* Guest rc, ignored */);
+ int vrc = pProcess->startProcess(30 * 1000 /* 30s timeout */,
+ NULL /* Guest rc, ignored */);
/* Nothing to do here anymore. */
- LogFlowFuncLeaveRC(vrc);
+ LogFlowFunc(("pProcess=%p returning rc=%Rrc\n", (GuestProcess *)pProcess, vrc));
return vrc;
}
-int GuestProcess::terminateProcess(void)
+int GuestProcess::terminateProcess(uint32_t uTimeoutMS, int *pGuestRc)
{
- LogFlowThisFuncEnter();
+ /* pGuestRc is optional. */
+ LogFlowThisFunc(("uTimeoutMS=%RU32\n", uTimeoutMS));
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- if (mData.mParent->getProtocolVersion() < 2)
- return VERR_NOT_SUPPORTED;
+ int vrc = VINF_SUCCESS;
- LogFlowThisFuncLeave();
- return VERR_NOT_IMPLEMENTED;
-}
+ if (mData.mStatus != ProcessStatus_Started)
+ {
+ LogFlowThisFunc(("Process not in started state (state is %RU32), skipping termination\n",
+ mData.mStatus));
+ }
+ else
+ {
+ AssertPtr(mSession);
+ /* Note: VBox < 4.3 (aka protocol version 1) does not
+ * support this, so just skip. */
+ if (mSession->getProtocolVersion() < 2)
+ vrc = VERR_NOT_SUPPORTED;
-int GuestProcess::waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, ProcessWaitResult_T &waitResult, int *pGuestRc)
-{
- LogFlowThisFuncEnter();
+ if (RT_SUCCESS(vrc))
+ {
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
+ try
+ {
+ eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
- AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
+ vrc = registerWaitEvent(eventTypes, &pEvent);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
- LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n",
- fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));
+ if (RT_FAILURE(vrc))
+ return vrc;
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ VBOXHGCMSVCPARM paParms[4];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setUInt32(mData.mPID);
- /* Did some error occur before? Then skip waiting and return. */
- if (mData.mStatus == ProcessStatus_Error)
- {
- waitResult = ProcessWaitResult_Error;
- AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest process indicated an error\n", mData.mRC));
- if (pGuestRc)
- *pGuestRc = mData.mRC; /* Return last set error. */
- return VERR_GENERAL_FAILURE; /** @todo Special guest control rc needed! */
+ alock.release(); /* Drop the write lock before sending. */
+
+ vrc = sendCommand(HOST_EXEC_TERMINATE, i, paParms);
+ if (RT_SUCCESS(vrc))
+ vrc = waitForStatusChange(pEvent, uTimeoutMS,
+ NULL /* ProcessStatus */, pGuestRc);
+ unregisterWaitEvent(pEvent);
+ }
}
- waitResult = ProcessWaitResult_None;
- if ( (fWaitFlags & ProcessWaitForFlag_Terminate)
- || (fWaitFlags & ProcessWaitForFlag_StdIn)
- || (fWaitFlags & ProcessWaitForFlag_StdOut)
- || (fWaitFlags & ProcessWaitForFlag_StdErr))
- {
- switch (mData.mStatus)
- {
- case ProcessStatus_TerminatedNormally:
- case ProcessStatus_TerminatedSignal:
- case ProcessStatus_TerminatedAbnormally:
- case ProcessStatus_Down:
- waitResult = ProcessWaitResult_Terminate;
- break;
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
- case ProcessStatus_TimedOutKilled:
- case ProcessStatus_TimedOutAbnormally:
- waitResult = ProcessWaitResult_Timeout;
- break;
+/* static */
+ProcessWaitResult_T GuestProcess::waitFlagsToResultEx(uint32_t fWaitFlags,
+ ProcessStatus_T oldStatus, ProcessStatus_T newStatus,
+ uint32_t uProcFlags, uint32_t uProtocol)
+{
+ ProcessWaitResult_T waitResult = ProcessWaitResult_None;
- case ProcessStatus_Error:
- /* Handled above. */
- break;
+ switch (newStatus)
+ {
+ case ProcessStatus_TerminatedNormally:
+ case ProcessStatus_TerminatedSignal:
+ case ProcessStatus_TerminatedAbnormally:
+ case ProcessStatus_Down:
+ /* Nothing to wait for anymore. */
+ waitResult = ProcessWaitResult_Terminate;
+ break;
- case ProcessStatus_Started:
+ case ProcessStatus_TimedOutKilled:
+ case ProcessStatus_TimedOutAbnormally:
+ /* Dito. */
+ waitResult = ProcessWaitResult_Timeout;
+ break;
+
+ case ProcessStatus_Started:
+ switch (oldStatus)
{
- /*
- * If ProcessCreateFlag_WaitForProcessStartOnly was specified on process creation the
- * caller is not interested in getting further process statuses -- so just don't notify
- * anything here anymore and return.
- */
- if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
+ case ProcessStatus_Undefined:
+ case ProcessStatus_Starting:
+ /* Also wait for process start. */
+ if (fWaitFlags & ProcessWaitForFlag_Start)
+ waitResult = ProcessWaitResult_Start;
+ else
+ {
+ /*
+ * If ProcessCreateFlag_WaitForProcessStartOnly was specified on process creation the
+ * caller is not interested in getting further process statuses -- so just don't notify
+ * anything here anymore and return.
+ */
+ if (uProcFlags & ProcessCreateFlag_WaitForProcessStartOnly)
+ waitResult = ProcessWaitResult_Start;
+ }
+ break;
+
+ case ProcessStatus_Started:
+ /* Only wait for process start. */
+ if (fWaitFlags == ProcessWaitForFlag_Start)
+ waitResult = ProcessWaitResult_Start;
+ break;
+
+ default:
+ AssertMsgFailed(("Unhandled old status %RU32 before new status 'started'\n",
+ oldStatus));
waitResult = ProcessWaitResult_Start;
- break;
+ break;
}
+ break;
- case ProcessStatus_Undefined:
- case ProcessStatus_Starting:
- /* Do the waiting below. */
- break;
+ case ProcessStatus_Error:
+ /* Nothing to wait for anymore. */
+ waitResult = ProcessWaitResult_Error;
+ break;
- default:
- AssertMsgFailed(("Unhandled process status %ld\n", mData.mStatus));
- return VERR_NOT_IMPLEMENTED;
- }
+ case ProcessStatus_Undefined:
+ case ProcessStatus_Starting:
+ /* No result available yet, leave wait
+ * flags untouched. */
+ break;
}
- else if (fWaitFlags & ProcessWaitForFlag_Start)
+
+ if (newStatus == ProcessStatus_Started)
{
- switch (mData.mStatus)
+ /* Filter out waits which are *not* supported using
+ * older guest control Guest Additions.
+ *
+ ** @todo ProcessWaitForFlag_Std* flags are not implemented yet.
+ */
+ if (uProtocol < 99) /* See @todo above. */
{
- case ProcessStatus_Started:
- case ProcessStatus_Paused:
- case ProcessStatus_Terminating:
- case ProcessStatus_TerminatedNormally:
- case ProcessStatus_TerminatedSignal:
- case ProcessStatus_TerminatedAbnormally:
- case ProcessStatus_Down:
- waitResult = ProcessWaitResult_Start;
- break;
+ if ( waitResult == ProcessWaitResult_None
+ /* We don't support waiting for stdin, out + err,
+ * just skip waiting then. */
+ && ( (fWaitFlags & ProcessWaitForFlag_StdIn)
+ || (fWaitFlags & ProcessWaitForFlag_StdOut)
+ || (fWaitFlags & ProcessWaitForFlag_StdErr)
+ )
+ )
+ {
+ /* Use _WaitFlagNotSupported because we don't know what to tell the caller. */
+ waitResult = ProcessWaitResult_WaitFlagNotSupported;
+ }
+ }
+ }
- case ProcessStatus_Error:
- waitResult = ProcessWaitResult_Error;
- break;
+#ifdef DEBUG
+ LogFlowFunc(("oldStatus=%RU32, newStatus=%RU32, fWaitFlags=0x%x, waitResult=%RU32\n",
+ oldStatus, newStatus, fWaitFlags, waitResult));
+#endif
+ return waitResult;
+}
- case ProcessStatus_TimedOutKilled:
- case ProcessStatus_TimedOutAbnormally:
- waitResult = ProcessWaitResult_Timeout;
- break;
+ProcessWaitResult_T GuestProcess::waitFlagsToResult(uint32_t fWaitFlags)
+{
+ AssertPtr(mSession);
+ return GuestProcess::waitFlagsToResultEx(fWaitFlags,
+ mData.mStatus /* curStatus */, mData.mStatus /* newStatus */,
+ mData.mProcess.mFlags, mSession->getProtocolVersion());
+}
- case ProcessStatus_Undefined:
- case ProcessStatus_Starting:
- /* Do the waiting below. */
- break;
+int GuestProcess::waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS,
+ ProcessWaitResult_T &waitResult, int *pGuestRc)
+{
+ AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
- default:
- AssertMsgFailed(("Unhandled process status %ld\n", mData.mStatus));
- return VERR_NOT_IMPLEMENTED;
- }
- }
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- /* Filter out waits which are *not* supported using
- * older guest control Guest Additions. */
- if (mData.mParent->getProtocolVersion() < 2)
+ LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, procStatus=%RU32, procRc=%Rrc, pGuestRc=%p\n",
+ fWaitFlags, uTimeoutMS, mData.mStatus, mData.mLastError, pGuestRc));
+
+ /* Did some error occur before? Then skip waiting and return. */
+ ProcessStatus_T curStatus = mData.mStatus;
+ if (curStatus == ProcessStatus_Error)
{
- if ( waitResult == ProcessWaitResult_None
- /* We don't support waiting for stdin, out + err,
- * just skip waiting then. */
- && ( (fWaitFlags & ProcessWaitForFlag_StdIn)
- || (fWaitFlags & ProcessWaitForFlag_StdOut)
- || (fWaitFlags & ProcessWaitForFlag_StdErr)
- )
- )
- {
- /* Use _WaitFlagNotSupported because we don't know what to tell the caller. */
- waitResult = ProcessWaitResult_WaitFlagNotSupported;
- }
+ waitResult = ProcessWaitResult_Error;
+ AssertMsg(RT_FAILURE(mData.mLastError), ("No error rc (%Rrc) set when guest process indicated an error\n", mData.mLastError));
+ if (pGuestRc)
+ *pGuestRc = mData.mLastError; /* Return last set error. */
+ LogFlowThisFunc(("Process is in error state (guestRc=%Rrc)\n", mData.mLastError));
+ return VERR_GSTCTL_GUEST_ERROR;
}
- LogFlowThisFunc(("procStatus=%ld, procRc=%Rrc, waitResult=%ld\n",
- mData.mStatus, mData.mRC, waitResult));
+ waitResult = waitFlagsToResult(fWaitFlags);
/* No waiting needed? Return immediately using the last set error. */
if (waitResult != ProcessWaitResult_None)
{
if (pGuestRc)
- *pGuestRc = mData.mRC; /* Return last set error (if any). */
- return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Special guest control rc needed! */
+ *pGuestRc = mData.mLastError; /* Return last set error (if any). */
+ LogFlowThisFunc(("Nothing to wait for (guestRc=%Rrc)\n", mData.mLastError));
+ return RT_SUCCESS(mData.mLastError) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
}
- if (mData.mWaitCount > 0)
- return VERR_ALREADY_EXISTS;
- mData.mWaitCount++;
+ /* Adjust timeout. Passing 0 means RT_INDEFINITE_WAIT. */
+ if (!uTimeoutMS)
+ uTimeoutMS = RT_INDEFINITE_WAIT;
- int vrc = VINF_SUCCESS;
+ int vrc;
+
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
try
{
- Assert(mData.mWaitEvent == NULL);
- mData.mWaitEvent = new GuestProcessWaitEvent(fWaitFlags);
+ eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
+
+ vrc = registerWaitEvent(eventTypes, &pEvent);
}
- catch(std::bad_alloc &)
+ catch (std::bad_alloc)
{
vrc = VERR_NO_MEMORY;
}
- if (RT_SUCCESS(vrc))
- {
- GuestProcessWaitEvent *pEvent = mData.mWaitEvent;
- AssertPtr(pEvent);
+ if (RT_FAILURE(vrc))
+ return vrc;
- alock.release(); /* Release lock before waiting. */
+ alock.release(); /* Release lock before waiting. */
+
+ /*
+ * Do the actual waiting.
+ */
+ ProcessStatus_T newStatus = ProcessStatus_Undefined;
+ uint64_t u64StartMS = RTTimeMilliTS();
+ for (;;)
+ {
+ uint64_t u64ElapsedMS = RTTimeMilliTS() - u64StartMS;
+ if ( uTimeoutMS != RT_INDEFINITE_WAIT
+ && u64ElapsedMS >= uTimeoutMS)
+ {
+ vrc = VERR_TIMEOUT;
+ break;
+ }
- vrc = pEvent->Wait(uTimeoutMS);
- LogFlowThisFunc(("Waiting completed with rc=%Rrc\n", vrc));
+ vrc = waitForStatusChange(pEvent,
+ uTimeoutMS == RT_INDEFINITE_WAIT
+ ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS,
+ &newStatus, pGuestRc);
if (RT_SUCCESS(vrc))
{
- waitResult = pEvent->GetWaitResult();
- int waitRc = pEvent->GetWaitRc();
+ alock.acquire();
+
+ waitResult = waitFlagsToResultEx(fWaitFlags, curStatus, newStatus,
+ mData.mProcess.mFlags, mSession->getProtocolVersion());
+#ifdef DEBUG
+ LogFlowThisFunc(("Got new status change: fWaitFlags=0x%x, newStatus=%RU32, waitResult=%RU32\n",
+ fWaitFlags, newStatus, waitResult));
+#endif
+ if (ProcessWaitResult_None != waitResult) /* We got a waiting result. */
+ break;
+ }
+ else /* Waiting failed, bail out. */
+ break;
+
+ alock.release(); /* Don't hold lock in next waiting round. */
+ }
+
+ unregisterWaitEvent(pEvent);
+
+ LogFlowThisFunc(("Returned waitResult=%RU32, newStatus=%RU32, rc=%Rrc\n",
+ waitResult, newStatus, vrc));
+ return vrc;
+}
+
+int GuestProcess::waitForInputNotify(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
+ ProcessInputStatus_T *pInputStatus, uint32_t *pcbProcessed)
+{
+ AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+
+ VBoxEventType_T evtType;
+ ComPtr<IEvent> pIEvent;
+ int vrc = waitForEvent(pEvent, uTimeoutMS,
+ &evtType, pIEvent.asOutParam());
+ if (RT_SUCCESS(vrc))
+ {
+ if (evtType == VBoxEventType_OnGuestProcessInputNotify)
+ {
+ ComPtr<IGuestProcessInputNotifyEvent> pProcessEvent = pIEvent;
+ Assert(!pProcessEvent.isNull());
- LogFlowThisFunc(("Waiting event returned rc=%Rrc\n", waitRc));
+ if (pInputStatus)
+ {
+ HRESULT hr2 = pProcessEvent->COMGETTER(Status)(pInputStatus);
+ ComAssertComRC(hr2);
+ }
+ if (pcbProcessed)
+ {
+ HRESULT hr2 = pProcessEvent->COMGETTER(Processed)((ULONG*)pcbProcessed);
+ ComAssertComRC(hr2);
+ }
+ }
+ else
+ vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
+ }
- if (pGuestRc)
- *pGuestRc = waitRc;
+ LogFlowThisFunc(("Returning pEvent=%p, uHandle=%RU32, rc=%Rrc\n",
+ pEvent, uHandle, vrc));
+ return vrc;
+}
- vrc = RT_SUCCESS(waitRc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Special guest control rc needed! */
+int GuestProcess::waitForOutput(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
+ void *pvData, size_t cbData, uint32_t *pcbRead)
+{
+ AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+ /* pvData is optional. */
+ /* cbData is optional. */
+ /* pcbRead is optional. */
+
+ LogFlowThisFunc(("cEventTypes=%zu, pEvent=%p, uHandle=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu, pcbRead=%p\n",
+ pEvent->TypeCount(), pEvent, uHandle, uTimeoutMS, pvData, cbData, pcbRead));
+
+ int vrc;
+
+ VBoxEventType_T evtType;
+ ComPtr<IEvent> pIEvent;
+ do
+ {
+ vrc = waitForEvent(pEvent, uTimeoutMS,
+ &evtType, pIEvent.asOutParam());
+ if (RT_SUCCESS(vrc))
+ {
+ if (evtType == VBoxEventType_OnGuestProcessOutput)
+ {
+ ComPtr<IGuestProcessOutputEvent> pProcessEvent = pIEvent;
+ Assert(!pProcessEvent.isNull());
+
+ ULONG uHandleEvent;
+ HRESULT hr = pProcessEvent->COMGETTER(Handle)(&uHandleEvent);
+ if ( SUCCEEDED(hr)
+ && uHandleEvent == uHandle)
+ {
+ if (pvData)
+ {
+ com::SafeArray <BYTE> data;
+ hr = pProcessEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data));
+ ComAssertComRC(hr);
+ size_t cbRead = data.size();
+ if (cbRead)
+ {
+ if (cbRead <= cbData)
+ {
+ /* Copy data from event into our buffer. */
+ memcpy(pvData, data.raw(), data.size());
+ }
+ else
+ vrc = VERR_BUFFER_OVERFLOW;
+
+ LogFlowThisFunc(("Read %zu bytes (uHandle=%RU32), rc=%Rrc\n",
+ cbRead, uHandleEvent, vrc));
+ }
+ }
+
+ if ( RT_SUCCESS(vrc)
+ && pcbRead)
+ {
+ ULONG cbRead;
+ hr = pProcessEvent->COMGETTER(Processed)(&cbRead);
+ ComAssertComRC(hr);
+ *pcbRead = (uint32_t)cbRead;
+ }
+
+ break;
+ }
+ else if (FAILED(hr))
+ vrc = VERR_COM_UNEXPECTED;
+ }
+ else
+ vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
}
- alock.acquire(); /* Get the lock again. */
+ } while (vrc == VINF_SUCCESS);
- /* Note: The caller always is responsible of deleting the
- * stuff it created before. See close() for more information. */
- delete mData.mWaitEvent;
- mData.mWaitEvent = NULL;
+ if ( vrc != VINF_SUCCESS
+ && pcbRead)
+ {
+ *pcbRead = 0;
}
- Assert(mData.mWaitCount);
- mData.mWaitCount--;
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int GuestProcess::waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
+ ProcessStatus_T *pProcessStatus, int *pGuestRc)
+{
+ AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+ /* pProcessStatus is optional. */
+ /* pGuestRc is optional. */
+
+ VBoxEventType_T evtType;
+ ComPtr<IEvent> pIEvent;
+ int vrc = waitForEvent(pEvent, uTimeoutMS,
+ &evtType, pIEvent.asOutParam());
+ if (RT_SUCCESS(vrc))
+ {
+ Assert(evtType == VBoxEventType_OnGuestProcessStateChanged);
+ ComPtr<IGuestProcessStateChangedEvent> pProcessEvent = pIEvent;
+ Assert(!pProcessEvent.isNull());
+
+ ProcessStatus_T procStatus;
+ HRESULT hr = pProcessEvent->COMGETTER(Status)(&procStatus);
+ ComAssertComRC(hr);
+ if (pProcessStatus)
+ *pProcessStatus = procStatus;
+
+ ComPtr<IVirtualBoxErrorInfo> errorInfo;
+ hr = pProcessEvent->COMGETTER(Error)(errorInfo.asOutParam());
+ ComAssertComRC(hr);
+
+ LONG lGuestRc;
+ hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
+ ComAssertComRC(hr);
+
+ LogFlowThisFunc(("Got procStatus=%RU32, guestRc=%RI32 (%Rrc)\n",
+ procStatus, lGuestRc, lGuestRc));
+
+ if (RT_FAILURE((int)lGuestRc))
+ vrc = VERR_GSTCTL_GUEST_ERROR;
+
+ if (pGuestRc)
+ *pGuestRc = (int)lGuestRc;
+ }
LogFlowFuncLeaveRC(vrc);
return vrc;
}
+/* static */
+bool GuestProcess::waitResultImpliesEx(ProcessWaitResult_T waitResult,
+ ProcessStatus_T procStatus, uint32_t uProcFlags,
+ uint32_t uProtocol)
+{
+ bool fImplies;
+
+ switch (waitResult)
+ {
+ case ProcessWaitResult_Start:
+ fImplies = procStatus == ProcessStatus_Started;
+ break;
+
+ case ProcessWaitResult_Terminate:
+ fImplies = ( procStatus == ProcessStatus_TerminatedNormally
+ || procStatus == ProcessStatus_TerminatedSignal
+ || procStatus == ProcessStatus_TerminatedAbnormally
+ || procStatus == ProcessStatus_TimedOutKilled
+ || procStatus == ProcessStatus_TimedOutAbnormally
+ || procStatus == ProcessStatus_Down
+ || procStatus == ProcessStatus_Error);
+ break;
+
+ default:
+ fImplies = false;
+ break;
+ }
+
+ return fImplies;
+}
+
int GuestProcess::writeData(uint32_t uHandle, uint32_t uFlags,
void *pvData, size_t cbData, uint32_t uTimeoutMS, uint32_t *puWritten, int *pGuestRc)
{
@@ -1511,107 +1724,64 @@ int GuestProcess::writeData(uint32_t uHandle, uint32_t uFlags,
return VINF_SUCCESS; /* Not available for writing (anymore). */
}
- int vrc = VINF_SUCCESS;
+ int vrc;
- GuestCtrlCallback *pCallbackWrite = NULL;
+ GuestWaitEvent *pEvent = NULL;
+ GuestEventTypes eventTypes;
try
{
- pCallbackWrite = new GuestCtrlCallback();
+ /*
+ * On Guest Additions < 4.3 there is no guarantee that the process status
+ * change arrives *after* the input event, e.g. if this was the last input
+ * block being written and the process will report status "terminate".
+ * So just skip checking for process status change and only wait for the
+ * input event.
+ */
+ if (mSession->getProtocolVersion() >= 2)
+ eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
+ eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
+
+ vrc = registerWaitEvent(eventTypes, &pEvent);
}
- catch(std::bad_alloc &)
+ catch (std::bad_alloc)
{
vrc = VERR_NO_MEMORY;
}
- /* Create callback and add it to the map. */
- uint32_t uContextID = 0;
- if (RT_SUCCESS(vrc))
- {
- vrc = pCallbackWrite->Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS);
- if (RT_SUCCESS(vrc))
- vrc = callbackAdd(pCallbackWrite, &uContextID);
- }
-
- alock.release(); /* Drop the write lock again. */
+ if (RT_FAILURE(vrc))
+ return vrc;
- if (RT_SUCCESS(vrc))
- {
- VBOXHGCMSVCPARM paParms[5];
+ VBOXHGCMSVCPARM paParms[5];
+ int i = 0;
+ paParms[i++].setUInt32(pEvent->ContextID());
+ paParms[i++].setUInt32(mData.mPID);
+ paParms[i++].setUInt32(uFlags);
+ paParms[i++].setPointer(pvData, (uint32_t)cbData);
+ paParms[i++].setUInt32((uint32_t)cbData);
- int i = 0;
- paParms[i++].setUInt32(uContextID);
- paParms[i++].setUInt32(mData.mPID);
- paParms[i++].setUInt32(uFlags);
- paParms[i++].setPointer(pvData, cbData);
- paParms[i++].setUInt32(cbData);
-
- vrc = sendCommand(HOST_EXEC_SET_INPUT, i, paParms);
- }
+ alock.release(); /* Drop the write lock before sending. */
+ uint32_t cbProcessed = 0;
+ vrc = sendCommand(HOST_EXEC_SET_INPUT, i, paParms);
if (RT_SUCCESS(vrc))
{
- /*
- * Let's wait for the process being started.
- * Note: Be sure not keeping a AutoRead/WriteLock here.
- */
- LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
- vrc = pCallbackWrite->Wait(uTimeoutMS);
- if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
+ ProcessInputStatus_T inputStatus;
+ vrc = waitForInputNotify(pEvent, uHandle, uTimeoutMS,
+ &inputStatus, &cbProcessed);
+ if (RT_SUCCESS(vrc))
{
- int guestRc = pCallbackWrite->GetResultCode();
- LogFlowThisFunc(("Callback returned rc=%Rrc, cbData=%RU32\n", guestRc, pCallbackWrite->GetDataSize()));
+ /** @todo Set guestRc. */
- if (RT_SUCCESS(guestRc))
- {
- Assert(pCallbackWrite->GetDataSize() == sizeof(CALLBACKDATAEXECINSTATUS));
- PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)pCallbackWrite->GetDataRaw();
- AssertPtr(pData);
-
- uint32_t cbWritten = 0;
- switch (pData->u32Status)
- {
- case INPUT_STS_WRITTEN:
- cbWritten = pData->cbProcessed;
- break;
-
- case INPUT_STS_ERROR:
- vrc = pData->u32Flags; /** @todo Fix int vs. uint32_t! */
- break;
-
- case INPUT_STS_TERMINATED:
- vrc = VERR_CANCELLED;
- break;
-
- case INPUT_STS_OVERFLOW:
- vrc = VERR_BUFFER_OVERFLOW;
- break;
-
- default:
- /* Silently skip unknown errors. */
- break;
- }
-
- LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
-
- if (pGuestRc)
- *pGuestRc = guestRc;
-
- if (puWritten)
- *puWritten = cbWritten;
-
- if (RT_FAILURE(guestRc))
- vrc = VERR_GENERAL_FAILURE; /** @todo Special guest control rc needed! */
- }
+ if (puWritten)
+ *puWritten = cbProcessed;
}
+ /** @todo Error handling. */
}
- alock.acquire();
-
- int rc2 = callbackRemove(uContextID);
- if (RT_SUCCESS(vrc))
- vrc = rc2;
+ unregisterWaitEvent(pEvent);
- LogFlowFuncLeaveRC(vrc);
+ LogFlowThisFunc(("Returning cbProcessed=%RU32, rc=%Rrc\n",
+ cbProcessed, vrc));
return vrc;
}
@@ -1623,6 +1793,8 @@ STDMETHODIMP GuestProcess::Read(ULONG aHandle, ULONG aToRead, ULONG aTimeoutMS,
#ifndef VBOX_WITH_GUEST_CONTROL
ReturnComNotImplemented();
#else
+ LogFlowThisFuncEnter();
+
if (aToRead == 0)
return setError(E_INVALIDARG, tr("The size to read is zero"));
CheckComArgOutSafeArrayPointerValid(aData);
@@ -1635,7 +1807,7 @@ STDMETHODIMP GuestProcess::Read(ULONG aHandle, ULONG aToRead, ULONG aTimeoutMS,
HRESULT hr = S_OK;
- size_t cbRead; int guestRc;
+ uint32_t cbRead; int guestRc;
int vrc = readData(aHandle, aToRead, aTimeoutMS, data.raw(), aToRead, &cbRead, &guestRc);
if (RT_SUCCESS(vrc))
{
@@ -1647,7 +1819,7 @@ STDMETHODIMP GuestProcess::Read(ULONG aHandle, ULONG aToRead, ULONG aTimeoutMS,
{
switch (vrc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
hr = GuestProcess::setErrorExternal(this, guestRc);
break;
@@ -1659,7 +1831,7 @@ STDMETHODIMP GuestProcess::Read(ULONG aHandle, ULONG aToRead, ULONG aTimeoutMS,
}
}
- LogFlowThisFunc(("rc=%Rrc, cbRead=%RU64\n", vrc, cbRead));
+ LogFlowThisFunc(("rc=%Rrc, cbRead=%RU32\n", vrc, cbRead));
LogFlowFuncLeaveRC(vrc);
return hr;
@@ -1678,14 +1850,16 @@ STDMETHODIMP GuestProcess::Terminate(void)
HRESULT hr = S_OK;
- int vrc = terminateProcess();
+ int guestRc;
+ int vrc = terminateProcess(30 * 1000 /* Timeout in ms */,
+ &guestRc);
if (RT_FAILURE(vrc))
{
switch (vrc)
{
- case VERR_NOT_IMPLEMENTED:
- ReturnComNotImplemented();
- break; /* Never reached. */
+ case VERR_GSTCTL_GUEST_ERROR:
+ hr = GuestProcess::setErrorExternal(this, guestRc);
+ break;
case VERR_NOT_SUPPORTED:
hr = setError(VBOX_E_IPRT_ERROR,
@@ -1701,15 +1875,12 @@ STDMETHODIMP GuestProcess::Terminate(void)
}
}
- AssertPtr(mData.mParent);
- mData.mParent->processRemoveFromList(this);
-
- /*
- * Release autocaller before calling uninit.
- */
- autoCaller.release();
-
- uninit();
+ /* Remove process from guest session list. Now only API clients
+ * still can hold references to it. */
+ AssertPtr(mSession);
+ int rc2 = mSession->processRemoveFromList(this);
+ if (RT_SUCCESS(vrc))
+ vrc = rc2;
LogFlowFuncLeaveRC(vrc);
return hr;
@@ -1743,7 +1914,7 @@ STDMETHODIMP GuestProcess::WaitFor(ULONG aWaitFlags, ULONG aTimeoutMS, ProcessWa
{
switch (vrc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
hr = GuestProcess::setErrorExternal(this, guestRc);
break;
@@ -1796,22 +1967,23 @@ STDMETHODIMP GuestProcess::Write(ULONG aHandle, ULONG aFlags,
#else
LogFlowThisFuncEnter();
+ CheckComArgSafeArrayNotNull(aData);
CheckComArgOutPointerValid(aWritten);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ com::SafeArray<BYTE> data(ComSafeArrayInArg(aData));
HRESULT hr = S_OK;
- com::SafeArray<BYTE> data(ComSafeArrayInArg(aData)); int guestRc;
- int vrc = writeData(aHandle, aFlags, data.raw(), data.size(), aTimeoutMS, (uint32_t*)aWritten, &guestRc);
+ uint32_t cbWritten; int guestRc;
+ int vrc = writeData(aHandle, aFlags, data.raw(), data.size(), aTimeoutMS, &cbWritten, &guestRc);
if (RT_FAILURE(vrc))
{
switch (vrc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
hr = GuestProcess::setErrorExternal(this, guestRc);
break;
@@ -1823,7 +1995,9 @@ STDMETHODIMP GuestProcess::Write(ULONG aHandle, ULONG aFlags,
}
}
- LogFlowThisFunc(("rc=%Rrc, aWritten=%RU32\n", vrc, aWritten));
+ LogFlowThisFunc(("rc=%Rrc, aWritten=%RU32\n", vrc, cbWritten));
+
+ *aWritten = (ULONG)cbWritten;
LogFlowFuncLeaveRC(vrc);
return hr;
@@ -1838,6 +2012,7 @@ STDMETHODIMP GuestProcess::WriteArray(ULONG aHandle, ComSafeArrayIn(ProcessInput
#else
LogFlowThisFuncEnter();
+ CheckComArgSafeArrayNotNull(aData);
CheckComArgOutPointerValid(aWritten);
AutoCaller autoCaller(this);
@@ -1858,13 +2033,14 @@ STDMETHODIMP GuestProcess::WriteArray(ULONG aHandle, ComSafeArrayIn(ProcessInput
///////////////////////////////////////////////////////////////////////////////
GuestProcessTool::GuestProcessTool(void)
- : pSession(NULL)
+ : pSession(NULL),
+ pProcess(NULL)
{
}
GuestProcessTool::~GuestProcessTool(void)
{
- Terminate();
+ Terminate(30 * 1000, NULL /* pGuestRc */);
}
int GuestProcessTool::Init(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo,
@@ -1875,7 +2051,7 @@ int GuestProcessTool::Init(GuestSession *pGuestSession, const GuestProcessStartu
AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER);
- pSession = pGuestSession;
+ pSession = pGuestSession;
mStartupInfo = startupInfo;
/* Make sure the process is hidden. */
@@ -1883,15 +2059,18 @@ int GuestProcessTool::Init(GuestSession *pGuestSession, const GuestProcessStartu
int vrc = pSession->processCreateExInteral(mStartupInfo, pProcess);
if (RT_SUCCESS(vrc))
- vrc = fAsync ? pProcess->startProcessAsync() : pProcess->startProcess(pGuestRc);
+ vrc = fAsync
+ ? pProcess->startProcessAsync()
+ : pProcess->startProcess(30 * 1000 /* 30s timeout */, pGuestRc);
- if ( !fAsync
+ if ( RT_SUCCESS(vrc)
+ && !fAsync
&& ( pGuestRc
&& RT_FAILURE(*pGuestRc)
)
)
{
- vrc = VERR_GENERAL_FAILURE; /** @todo Special guest control rc needed! */
+ vrc = VERR_GSTCTL_GUEST_ERROR;
}
LogFlowFuncLeaveRC(vrc);
@@ -1925,20 +2104,78 @@ int GuestProcessTool::GetCurrentBlock(uint32_t uHandle, GuestProcessStreamBlock
bool GuestProcessTool::IsRunning(void)
{
- AssertReturn(!pProcess.isNull(), true);
+ AssertReturn(!pProcess.isNull(), false);
ProcessStatus_T procStatus = ProcessStatus_Undefined;
HRESULT hr = pProcess->COMGETTER(Status(&procStatus));
Assert(SUCCEEDED(hr));
- if ( procStatus != ProcessStatus_Started
- && procStatus != ProcessStatus_Paused
- && procStatus != ProcessStatus_Terminating)
+ if ( procStatus == ProcessStatus_Started
+ || procStatus == ProcessStatus_Paused
+ || procStatus == ProcessStatus_Terminating)
{
- return false;
+ return true;
}
- return true;
+ return false;
+}
+
+/* static */
+int GuestProcessTool::Run( GuestSession *pGuestSession,
+ const GuestProcessStartupInfo &startupInfo,
+ int *pGuestRc)
+{
+ return RunEx(pGuestSession, startupInfo,
+ NULL /* pStrmOutObjects */, 0 /* cStrmOutObjects */,
+ pGuestRc);
+}
+
+/* static */
+int GuestProcessTool::RunEx( GuestSession *pGuestSession,
+ const GuestProcessStartupInfo &startupInfo,
+ GuestCtrlStreamObjects *pStrmOutObjects,
+ uint32_t cStrmOutObjects,
+ int *pGuestRc)
+{
+ GuestProcessTool procTool; int guestRc;
+ int vrc = procTool.Init(pGuestSession, startupInfo, false /* Async */, &guestRc);
+ if (RT_SUCCESS(vrc))
+ {
+ while (cStrmOutObjects--)
+ {
+ try
+ {
+ GuestProcessStreamBlock strmBlk;
+ vrc = procTool.WaitEx( pStrmOutObjects
+ ? GUESTPROCESSTOOL_FLAG_STDOUT_BLOCK
+ : GUESTPROCESSTOOL_FLAG_NONE, &strmBlk, &guestRc);
+ if (pStrmOutObjects)
+ pStrmOutObjects->push_back(strmBlk);
+ }
+ catch (std::bad_alloc)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+ }
+ }
+
+ if (RT_SUCCESS(vrc))
+ {
+ /* Make sure the process runs until completion. */
+ vrc = procTool.Wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
+ if (RT_SUCCESS(vrc))
+ {
+ guestRc = procTool.TerminatedOk(NULL /* Exit code */);
+ if (RT_FAILURE(guestRc))
+ vrc = VERR_GSTCTL_GUEST_ERROR;
+ }
+ }
+
+ if (pGuestRc)
+ *pGuestRc = guestRc;
+
+ LogFlowFunc(("Returned rc=%Rrc, guestRc=%Rrc\n", vrc, guestRc));
+ return vrc;
}
int GuestProcessTool::TerminatedOk(LONG *pExitCode)
@@ -1946,6 +2183,7 @@ int GuestProcessTool::TerminatedOk(LONG *pExitCode)
Assert(!pProcess.isNull());
/* pExitCode is optional. */
+ int vrc;
if (!IsRunning())
{
LONG exitCode;
@@ -1955,36 +2193,36 @@ int GuestProcessTool::TerminatedOk(LONG *pExitCode)
if (pExitCode)
*pExitCode = exitCode;
- if (exitCode != 0)
- return VERR_NOT_EQUAL; /** @todo Special guest control rc needed! */
- return VINF_SUCCESS;
+ vrc = (exitCode != 0)
+ /** @todo Special guest control rc needed! */
+ ? VERR_NOT_EQUAL : VINF_SUCCESS;
}
+ else
+ vrc = VERR_INVALID_STATE; /** @todo Special guest control rc needed! */
- return VERR_INVALID_STATE; /** @todo Special guest control rc needed! */
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
}
int GuestProcessTool::Wait(uint32_t fFlags, int *pGuestRc)
{
- return WaitEx(fFlags, NULL /* pStreamBlock */, pGuestRc);
+ return WaitEx(fFlags, NULL /* pStrmBlkOut */, pGuestRc);
}
-int GuestProcessTool::WaitEx(uint32_t fFlags, GuestProcessStreamBlock *pStreamBlock, int *pGuestRc)
+int GuestProcessTool::WaitEx(uint32_t fFlags, GuestProcessStreamBlock *pStrmBlkOut, int *pGuestRc)
{
- LogFlowThisFunc(("pSession=%p, fFlags=0x%x, pStreamBlock=%p, pGuestRc=%p\n",
- pSession, fFlags, pStreamBlock, pGuestRc));
-
- AssertPtrReturn(pSession, VERR_INVALID_POINTER);
- Assert(!pProcess.isNull());
- /* Other parameters are optional. */
+ LogFlowThisFunc(("fFlags=0x%x, pStreamBlock=%p, pGuestRc=%p\n",
+ fFlags, pStrmBlkOut, pGuestRc));
/* Can we parse the next block without waiting? */
int vrc;
if (fFlags & GUESTPROCESSTOOL_FLAG_STDOUT_BLOCK)
{
- AssertPtr(pStreamBlock);
- vrc = GetCurrentBlock(OUTPUT_HANDLE_ID_STDOUT, *pStreamBlock);
+ AssertPtr(pStrmBlkOut);
+ vrc = GetCurrentBlock(OUTPUT_HANDLE_ID_STDOUT, *pStrmBlkOut);
if (RT_SUCCESS(vrc))
return vrc;
+ /* else do the the waiting below. */
}
/* Do the waiting. */
@@ -1994,25 +2232,47 @@ int GuestProcessTool::WaitEx(uint32_t fFlags, GuestProcessStreamBlock *pStreamBl
if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
fWaitFlags |= ProcessWaitForFlag_StdErr;
- LogFlowFunc(("waitFlags=0x%x\n", fWaitFlags));
-
- /** @todo Decrease timeout. */
+ /** @todo Decrease timeout while running. */
+ uint64_t u64StartMS = RTTimeMilliTS();
uint32_t uTimeoutMS = mStartupInfo.mTimeoutMS;
int guestRc;
bool fDone = false;
BYTE byBuf[_64K];
- size_t cbRead;
+ uint32_t cbRead;
bool fHandleStdOut = false;
bool fHandleStdErr = false;
+ /**
+ * Updates the elapsed time and checks if a
+ * timeout happened, then breaking out of the loop.
+ */
+#define UPDATE_AND_CHECK_ELAPSED_TIME() \
+ u64ElapsedMS = RTTimeMilliTS() - u64StartMS; \
+ if ( uTimeoutMS != RT_INDEFINITE_WAIT \
+ && u64ElapsedMS >= uTimeoutMS) \
+ { \
+ vrc = VERR_TIMEOUT; \
+ break; \
+ }
+
+ /**
+ * Returns the remaining time (in ms).
+ */
+#define GET_REMAINING_TIME \
+ uTimeoutMS == RT_INDEFINITE_WAIT \
+ ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS \
+
ProcessWaitResult_T waitRes;
do
{
- vrc = pProcess->waitFor(fWaitFlags,
- uTimeoutMS, waitRes, &guestRc);
+ uint64_t u64ElapsedMS;
+ UPDATE_AND_CHECK_ELAPSED_TIME();
+
+ vrc = pProcess->waitFor(fWaitFlags, GET_REMAINING_TIME,
+ waitRes, &guestRc);
if (RT_FAILURE(vrc))
break;
@@ -2041,7 +2301,7 @@ int GuestProcessTool::WaitEx(uint32_t fFlags, GuestProcessStreamBlock *pStreamBl
break;
case ProcessWaitResult_Error:
- vrc = VERR_GENERAL_FAILURE; /** @todo Special guest control rc needed! */
+ vrc = VERR_GSTCTL_GUEST_ERROR;
break;
case ProcessWaitResult_Terminate:
@@ -2058,28 +2318,39 @@ int GuestProcessTool::WaitEx(uint32_t fFlags, GuestProcessStreamBlock *pStreamBl
break;
default:
- AssertReleaseMsgFailed(("Unhandled process wait result %ld\n", waitRes));
+ AssertMsgFailed(("Unhandled process wait result %RU32\n", waitRes));
break;
}
+ if (RT_FAILURE(vrc))
+ break;
+
if (fHandleStdOut)
{
+ UPDATE_AND_CHECK_ELAPSED_TIME();
+
+ cbRead = 0;
vrc = pProcess->readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
- uTimeoutMS, byBuf, sizeof(byBuf),
+ GET_REMAINING_TIME,
+ byBuf, sizeof(byBuf),
&cbRead, &guestRc);
- if (RT_FAILURE(vrc))
+ if ( RT_FAILURE(vrc)
+ || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
break;
if (cbRead)
{
- LogFlowThisFunc(("Received %RU64 bytes from stdout\n", cbRead));
+ LogFlowThisFunc(("Received %RU32 bytes from stdout\n", cbRead));
vrc = mStdOut.AddData(byBuf, cbRead);
if ( RT_SUCCESS(vrc)
&& (fFlags & GUESTPROCESSTOOL_FLAG_STDOUT_BLOCK))
{
- AssertPtr(pStreamBlock);
- vrc = GetCurrentBlock(OUTPUT_HANDLE_ID_STDOUT, *pStreamBlock);
+ AssertPtr(pStrmBlkOut);
+ vrc = GetCurrentBlock(OUTPUT_HANDLE_ID_STDOUT, *pStrmBlkOut);
+
+ /* When successful, break out of the loop because we're done
+ * with reading the first stream block. */
if (RT_SUCCESS(vrc))
fDone = true;
}
@@ -2090,15 +2361,20 @@ int GuestProcessTool::WaitEx(uint32_t fFlags, GuestProcessStreamBlock *pStreamBl
if (fHandleStdErr)
{
+ UPDATE_AND_CHECK_ELAPSED_TIME();
+
+ cbRead = 0;
vrc = pProcess->readData(OUTPUT_HANDLE_ID_STDERR, sizeof(byBuf),
- uTimeoutMS, byBuf, sizeof(byBuf),
+ GET_REMAINING_TIME,
+ byBuf, sizeof(byBuf),
&cbRead, &guestRc);
- if (RT_FAILURE(vrc))
+ if ( RT_FAILURE(vrc)
+ || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
break;
if (cbRead)
{
- LogFlowThisFunc(("Received %RU64 bytes from stderr\n", cbRead));
+ LogFlowThisFunc(("Received %RU32 bytes from stderr\n", cbRead));
vrc = mStdErr.AddData(byBuf, cbRead);
}
@@ -2107,7 +2383,13 @@ int GuestProcessTool::WaitEx(uint32_t fFlags, GuestProcessStreamBlock *pStreamBl
} while (!fDone && RT_SUCCESS(vrc));
- LogFlowThisFunc(("Loop ended with rc=%Rrc, guestRc=%Rrc, waitRes=%ld\n",
+#undef UPDATE_AND_CHECK_ELAPSED_TIME
+#undef GET_REMAINING_TIME
+
+ if (RT_FAILURE(guestRc))
+ vrc = VERR_GSTCTL_GUEST_ERROR;
+
+ LogFlowThisFunc(("Loop ended with rc=%Rrc, guestRc=%Rrc, waitRes=%RU32\n",
vrc, guestRc, waitRes));
if (pGuestRc)
*pGuestRc = guestRc;
@@ -2116,21 +2398,20 @@ int GuestProcessTool::WaitEx(uint32_t fFlags, GuestProcessStreamBlock *pStreamBl
return vrc;
}
-void GuestProcessTool::Terminate(void)
+int GuestProcessTool::Terminate(uint32_t uTimeoutMS, int *pGuestRc)
{
LogFlowThisFuncEnter();
+ int rc = VINF_SUCCESS;
if (!pProcess.isNull())
{
- /** @todo Add pProcess.Terminate() here as soon as it's implemented. */
-
- Assert(pSession);
- int rc2 = pSession->processRemoveFromList(pProcess);
- AssertRC(rc2);
-
+ rc = pProcess->terminateProcess(uTimeoutMS, pGuestRc);
pProcess.setNull();
}
+ else
+ rc = VERR_NOT_FOUND;
- LogFlowThisFuncLeave();
+ LogFlowFuncLeaveRC(rc);
+ return rc;
}
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 */
+}
+
diff --git a/src/VBox/Main/src-client/GuestSessionImplTasks.cpp b/src/VBox/Main/src-client/GuestSessionImplTasks.cpp
index 84866b30..759020d9 100644
--- a/src/VBox/Main/src-client/GuestSessionImplTasks.cpp
+++ b/src/VBox/Main/src-client/GuestSessionImplTasks.cpp
@@ -1,11 +1,10 @@
-
/* $Id: GuestSessionImplTasks.cpp $ */
/** @file
- * VirtualBox Main - XXX.
+ * VirtualBox Main - Guest session tasks.
*/
/*
- * 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;
@@ -138,6 +137,9 @@ int GuestSessionTask::setProgressSuccess(void)
HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg)
{
+ LogFlowFunc(("hr=%Rhrc, strMsg=%s\n",
+ hr, strMsg.c_str()));
+
if (mProgress.isNull()) /* Progress is optional. */
return hr; /* Return original rc. */
@@ -158,6 +160,62 @@ HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg)
return hr; /* Return original rc. */
}
+SessionTaskOpen::SessionTaskOpen(GuestSession *pSession,
+ uint32_t uFlags,
+ uint32_t uTimeoutMS)
+ : GuestSessionTask(pSession),
+ mFlags(uFlags),
+ mTimeoutMS(uTimeoutMS)
+{
+
+}
+
+SessionTaskOpen::~SessionTaskOpen(void)
+{
+
+}
+
+int SessionTaskOpen::Run(int *pGuestRc)
+{
+ LogFlowThisFuncEnter();
+
+ ComObjPtr<GuestSession> pSession = mSession;
+ Assert(!pSession.isNull());
+
+ AutoCaller autoCaller(pSession);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ int vrc = pSession->startSessionInternal(pGuestRc);
+ /* Nothing to do here anymore. */
+
+ LogFlowFuncLeaveRC(vrc);
+ return vrc;
+}
+
+int SessionTaskOpen::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
+{
+ LogFlowThisFunc(("strDesc=%s\n", strDesc.c_str()));
+
+ mDesc = strDesc;
+ mProgress = pProgress;
+
+ int rc = RTThreadCreate(NULL, SessionTaskOpen::taskThread, this,
+ 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
+ "gctlSesOpen");
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/* static */
+int SessionTaskOpen::taskThread(RTTHREAD Thread, void *pvUser)
+{
+ std::auto_ptr<SessionTaskOpen> task(static_cast<SessionTaskOpen*>(pvUser));
+ AssertReturn(task.get(), VERR_GENERAL_FAILURE);
+
+ LogFlowFunc(("pTask=%p\n", task.get()));
+ return task->Run(NULL /* guestRc */);
+}
+
SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
: GuestSessionTask(pSession),
@@ -245,6 +303,7 @@ int SessionTaskCopyTo::Run(void)
}
else
{
+ rc = VINF_SUCCESS;
pFile = mSourceFile;
/* Size + offset are optional. */
}
@@ -258,14 +317,20 @@ int SessionTaskCopyTo::Run(void)
/* Startup process. */
ComObjPtr<GuestProcess> pProcess; int guestRc;
- rc = pSession->processCreateExInteral(procInfo, pProcess);
if (RT_SUCCESS(rc))
- rc = pProcess->startProcess(&guestRc);
+ rc = pSession->processCreateExInteral(procInfo, pProcess);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(!pProcess.isNull());
+ rc = pProcess->startProcess(30 * 1000 /* 30s timeout */,
+ &guestRc);
+ }
+
if (RT_FAILURE(rc))
{
switch (rc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
setProgressErrorMsg(VBOX_E_IPRT_ERROR,
GuestProcess::guestErrorToString(guestRc));
break;
@@ -301,7 +366,7 @@ int SessionTaskCopyTo::Run(void)
/* If the guest does not support waiting for stdin, we now yield in
* order to reduce the CPU load due to busy waiting. */
if (waitRes == ProcessWaitResult_WaitFlagNotSupported)
- RTThreadSleep(1); /* Optional, don't check rc. */
+ RTThreadYield(); /* Optional, don't check rc. */
size_t cbRead = 0;
if (mSourceSize) /* If we have nothing to write, take a shortcut. */
@@ -312,7 +377,7 @@ int SessionTaskCopyTo::Run(void)
if (RT_SUCCESS(rc))
{
rc = RTFileRead(*pFile, (uint8_t*)byBuf,
- RT_MIN(cbToRead, sizeof(byBuf)), &cbRead);
+ RT_MIN((size_t)cbToRead, sizeof(byBuf)), &cbRead);
/*
* Some other error occured? There might be a chance that RTFileRead
* could not resolve/map the native error code to an IPRT code, so just
@@ -360,7 +425,7 @@ int SessionTaskCopyTo::Run(void)
{
switch (rc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
setProgressErrorMsg(VBOX_E_IPRT_ERROR,
GuestProcess::guestErrorToString(guestRc));
break;
@@ -403,7 +468,8 @@ int SessionTaskCopyTo::Run(void)
break;
} /* for */
- LogFlowThisFunc(("Copy loop ended with rc=%Rrc\n" ,rc));
+ LogFlowThisFunc(("Copy loop ended with rc=%Rrc, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
+ rc, cbToRead, cbWrittenTotal, mSourceSize));
if ( !fCanceled
|| RT_SUCCESS(rc))
@@ -471,9 +537,6 @@ int SessionTaskCopyTo::Run(void)
rc = setProgressSuccess();
}
}
-
- if (!pProcess.isNull())
- pProcess->uninit();
} /* processCreateExInteral */
if (!mSourceFile) /* Only close locally opened files. */
@@ -536,7 +599,7 @@ int SessionTaskCopyFrom::Run(void)
* Note: There will be races between querying file size + reading the guest file's
* content because we currently *do not* lock down the guest file when doing the
* actual operations.
- ** @todo Implement guest file locking!
+ ** @todo Use the IGuestFile API for locking down the file on the guest!
*/
GuestFsObjData objData; int guestRc;
int rc = pSession->fileQueryInfoInternal(Utf8Str(mSource), objData, &guestRc);
@@ -567,8 +630,8 @@ int SessionTaskCopyFrom::Run(void)
else
{
GuestProcessStartupInfo procInfo;
- procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" from guest to the host to \"%s\" (%RI64 bytes)"),
- mSource.c_str(), mDest.c_str(), objData.mObjectSize);
+ procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" from guest to the host to \"%s\" (%RI64 bytes)"),
+ mSource.c_str(), mDest.c_str(), objData.mObjectSize);
procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_CAT);
procInfo.mFlags = ProcessCreateFlag_Hidden | ProcessCreateFlag_WaitForStdOut;
@@ -579,12 +642,13 @@ int SessionTaskCopyFrom::Run(void)
ComObjPtr<GuestProcess> pProcess;
rc = pSession->processCreateExInteral(procInfo, pProcess);
if (RT_SUCCESS(rc))
- rc = pProcess->startProcess(&guestRc);
+ rc = pProcess->startProcess(30 * 1000 /* 30s timeout */,
+ &guestRc);
if (RT_FAILURE(rc))
{
switch (rc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
setProgressErrorMsg(VBOX_E_IPRT_ERROR,
GuestProcess::guestErrorToString(guestRc));
break;
@@ -613,7 +677,7 @@ int SessionTaskCopyFrom::Run(void)
{
switch (rc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
setProgressErrorMsg(VBOX_E_IPRT_ERROR,
GuestProcess::guestErrorToString(guestRc));
break;
@@ -634,9 +698,9 @@ int SessionTaskCopyFrom::Run(void)
/* If the guest does not support waiting for stdin, we now yield in
* order to reduce the CPU load due to busy waiting. */
if (waitRes == ProcessWaitResult_WaitFlagNotSupported)
- RTThreadSleep(1); /* Optional, don't check rc. */
+ RTThreadYield(); /* Optional, don't check rc. */
- size_t cbRead;
+ uint32_t cbRead = 0; /* readData can return with VWRN_GSTCTL_OBJECTSTATE_CHANGED. */
rc = pProcess->readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
30 * 1000 /* Timeout */, byBuf, sizeof(byBuf),
&cbRead, &guestRc);
@@ -644,7 +708,7 @@ int SessionTaskCopyFrom::Run(void)
{
switch (rc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
setProgressErrorMsg(VBOX_E_IPRT_ERROR,
GuestProcess::guestErrorToString(guestRc));
break;
@@ -711,7 +775,7 @@ int SessionTaskCopyFrom::Run(void)
/* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
* to the destination -> access denied. */
setProgressErrorMsg(VBOX_E_IPRT_ERROR,
- Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to \"%s\""),
+ Utf8StrFmt(GuestSession::tr("Unable to write \"%s\" to \"%s\": Access denied"),
mSource.c_str(), mDest.c_str()));
rc = VERR_GENERAL_FAILURE; /* Fudge. */
}
@@ -742,9 +806,6 @@ int SessionTaskCopyFrom::Run(void)
rc = setProgressSuccess();
}
}
-
- if (!pProcess.isNull())
- pProcess->uninit();
}
RTFileClose(fileDest);
@@ -781,10 +842,13 @@ int SessionTaskCopyFrom::taskThread(RTTHREAD Thread, void *pvUser)
}
SessionTaskUpdateAdditions::SessionTaskUpdateAdditions(GuestSession *pSession,
- const Utf8Str &strSource, uint32_t uFlags)
+ const Utf8Str &strSource,
+ const ProcessArguments &aArguments,
+ uint32_t uFlags)
: GuestSessionTask(pSession)
{
mSource = strSource;
+ mArguments = aArguments;
mFlags = uFlags;
}
@@ -793,6 +857,45 @@ SessionTaskUpdateAdditions::~SessionTaskUpdateAdditions(void)
}
+int SessionTaskUpdateAdditions::addProcessArguments(ProcessArguments &aArgumentsDest,
+ const ProcessArguments &aArgumentsSource)
+{
+ int rc = VINF_SUCCESS;
+
+ try
+ {
+ /* Filter out arguments which already are in the destination to
+ * not end up having them specified twice. Not the fastest method on the
+ * planet but does the job. */
+ ProcessArguments::const_iterator itSource = aArgumentsSource.begin();
+ while (itSource != aArgumentsSource.end())
+ {
+ bool fFound = false;
+ ProcessArguments::iterator itDest = aArgumentsDest.begin();
+ while (itDest != aArgumentsDest.end())
+ {
+ if ((*itDest).equalsIgnoreCase((*itSource)))
+ {
+ fFound = true;
+ break;
+ }
+ itDest++;
+ }
+
+ if (!fFound)
+ aArgumentsDest.push_back((*itSource));
+
+ itSource++;
+ }
+ }
+ catch(std::bad_alloc &)
+ {
+ return VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
Utf8Str const &strFileSource, const Utf8Str &strFileDest,
bool fOptional, uint32_t *pcbSize)
@@ -820,8 +923,8 @@ int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFS
/* Copy over the Guest Additions file to the guest. */
if (RT_SUCCESS(rc))
{
- LogFlowThisFunc(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
- strFileSource.c_str(), strFileDest.c_str()));
+ LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
+ strFileSource.c_str(), strFileDest.c_str()));
if (RT_SUCCESS(rc))
{
@@ -858,8 +961,8 @@ int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFS
/* Determine where the installer image ended up and if it has the correct size. */
if (RT_SUCCESS(rc))
{
- LogFlowThisFunc(("Verifying Guest Additions installer file \"%s\" ...\n",
- strFileDest.c_str()));
+ LogRel(("Verifying Guest Additions installer file \"%s\" ...\n",
+ strFileDest.c_str()));
GuestFsObjData objData;
int64_t cbSizeOnGuest; int guestRc;
@@ -874,15 +977,15 @@ int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFS
{
if (RT_SUCCESS(rc)) /* Size does not match. */
{
- LogFlowThisFunc(("Size of Guest Additions installer file \"%s\" does not match: %RI64bytes copied, %RU64bytes expected\n",
- strFileDest.c_str(), cbSizeOnGuest, cbSize));
+ LogRel(("Size of Guest Additions installer file \"%s\" does not match: %RI64 bytes copied, %RU64 bytes expected\n",
+ strFileDest.c_str(), cbSizeOnGuest, cbSize));
rc = VERR_BROKEN_PIPE; /** @todo Find a better error. */
}
else
{
switch (rc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
setProgressErrorMsg(VBOX_E_IPRT_ERROR,
GuestProcess::guestErrorToString(guestRc));
break;
@@ -899,7 +1002,7 @@ int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFS
if (RT_SUCCESS(rc))
{
if (pcbSize)
- *pcbSize = cbSizeOnGuest;
+ *pcbSize = (uint32_t)cbSizeOnGuest;
}
}
@@ -933,7 +1036,7 @@ int SessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, GuestProc
procInfo.mCommand.c_str(), exitCode));
break;
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
+ case VERR_GSTCTL_GUEST_ERROR:
setProgressErrorMsg(VBOX_E_IPRT_ERROR,
GuestProcess::guestErrorToString(guestRc));
break;
@@ -1036,7 +1139,7 @@ int SessionTaskUpdateAdditions::Run(void)
}
Utf8Str strOSVer;
- eOSType osType;
+ eOSType osType = eOSType_Unknown;
if (RT_SUCCESS(rc))
{
/*
@@ -1066,21 +1169,30 @@ int SessionTaskUpdateAdditions::Run(void)
* can't do automated updates here. */
/* Windows XP 64-bit (5.2) is a Windows 2003 Server actually, so skip this here. */
if ( RT_SUCCESS(rc)
- && ( strOSVer.startsWith("5.0") /* Exclude the build number. */
- || strOSVer.startsWith("5.1")) /* Exclude the build number. */
- )
+ && RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
{
- /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
- * because the Windows Guest Additions installer will fail because of WHQL popups. If the
- * flag is set this update routine ends successfully as soon as the installer was started
- * (and the user has to deal with it in the guest). */
- if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
+ if ( strOSVer.startsWith("5.0") /* Exclude the build number. */
+ || strOSVer.startsWith("5.1") /* Exclude the build number. */)
{
- hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
- Utf8StrFmt(GuestSession::tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
- rc = VERR_NOT_SUPPORTED;
+ /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
+ * because the Windows Guest Additions installer will fail because of WHQL popups. If the
+ * flag is set this update routine ends successfully as soon as the installer was started
+ * (and the user has to deal with it in the guest). */
+ if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
+ {
+ hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
+ Utf8StrFmt(GuestSession::tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
+ rc = VERR_NOT_SUPPORTED;
+ }
}
}
+ else
+ {
+ hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
+ Utf8StrFmt(GuestSession::tr("%s (%s) not supported for automatic updating, please update manually"),
+ strOSType.c_str(), strOSVer.c_str()));
+ rc = VERR_NOT_SUPPORTED;
+ }
}
else if (strOSType.contains("Solaris", Utf8Str::CaseInsensitive))
{
@@ -1090,7 +1202,8 @@ int SessionTaskUpdateAdditions::Run(void)
osType = eOSType_Linux;
#if 1 /* Only Windows is supported (and tested) at the moment. */
- if (osType != eOSType_Windows)
+ if ( RT_SUCCESS(rc)
+ && osType != eOSType_Windows)
{
hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
@@ -1172,15 +1285,15 @@ int SessionTaskUpdateAdditions::Run(void)
{
switch (rc)
{
- case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
- setProgressErrorMsg(VBOX_E_IPRT_ERROR,
- GuestProcess::guestErrorToString(guestRc));
+ case VERR_GSTCTL_GUEST_ERROR:
+ hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
+ GuestProcess::guestErrorToString(guestRc));
break;
default:
- setProgressErrorMsg(VBOX_E_IPRT_ERROR,
- Utf8StrFmt(GuestSession::tr("Error creating installation directory \"%s\" on the guest: %Rrc"),
- strUpdateDir.c_str(), rc));
+ hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
+ Utf8StrFmt(GuestSession::tr("Error creating installation directory \"%s\" on the guest: %Rrc"),
+ strUpdateDir.c_str(), rc));
break;
}
}
@@ -1262,6 +1375,10 @@ int SessionTaskUpdateAdditions::Run(void)
* using a running VBoxTray instance via balloon messages in the
* Windows taskbar. */
siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
+ /* Add optional installer command line arguments from the API to the
+ * installer's startup info. */
+ rc = addProcessArguments(siInstaller.mArguments, mArguments);
+ AssertRC(rc);
/* If the caller does not want to wait for out guest update process to end,
* complete the progress object now so that the caller can do other work. */
if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
@@ -1373,25 +1490,23 @@ int SessionTaskUpdateAdditions::Run(void)
Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", rc);
if (!mProgress.isNull()) /* Progress object is optional. */
{
- ComPtr<IVirtualBoxErrorInfo> pError;
- hr = mProgress->COMGETTER(ErrorInfo)(pError.asOutParam());
- Assert(!pError.isNull());
- if (SUCCEEDED(hr))
+ com::ProgressErrorInfo errorInfo(mProgress);
+ if ( errorInfo.isFullAvailable()
+ || errorInfo.isBasicAvailable())
{
- Bstr strVal;
- hr = pError->COMGETTER(Text)(strVal.asOutParam());
- if ( SUCCEEDED(hr)
- && strVal.isNotEmpty())
- strError = strVal;
+ strError = errorInfo.getText();
}
}
- LogRel(("Automatic update of Guest Additions failed: %s\n", strError.c_str()));
+ LogRel(("Automatic update of Guest Additions failed: %s (%Rhrc)\n",
+ strError.c_str(), hr));
}
LogRel(("Please install Guest Additions manually\n"));
}
+ /** @todo Clean up copied / left over installation files. */
+
LogFlowFuncLeaveRC(rc);
return rc;
}
diff --git a/src/VBox/Main/src-client/HGCM.cpp b/src/VBox/Main/src-client/HGCM.cpp
index abc74a27..722333ff 100644
--- a/src/VBox/Main/src-client/HGCM.cpp
+++ b/src/VBox/Main/src-client/HGCM.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2007 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;
@@ -242,7 +242,7 @@ HGCMService::HGCMService ()
#endif
m_hExtension (NULL)
{
- memset (&m_fntable, 0, sizeof (m_fntable));
+ RT_ZERO(m_fntable);
}
@@ -254,7 +254,7 @@ static bool g_fSaveState = false;
*
* @return VBox code
*/
-int HGCMService::loadServiceDLL (void)
+int HGCMService::loadServiceDLL(void)
{
LogFlowFunc(("m_pszSvcLibrary = %s\n", m_pszSvcLibrary));
@@ -264,9 +264,14 @@ int HGCMService::loadServiceDLL (void)
}
RTERRINFOSTATIC ErrInfo;
- RTErrInfoInitStatic (&ErrInfo);
+ RTErrInfoInitStatic(&ErrInfo);
- int rc = SUPR3HardenedLdrLoadAppPriv (m_pszSvcLibrary, &m_hLdrMod, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);
+ int rc;
+
+ if (RTPathHasPath(m_pszSvcLibrary))
+ rc = SUPR3HardenedLdrLoadPlugIn(m_pszSvcLibrary, &m_hLdrMod, &ErrInfo.Core);
+ else
+ rc = SUPR3HardenedLdrLoadAppPriv(m_pszSvcLibrary, &m_hLdrMod, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);
if (RT_SUCCESS(rc))
{
@@ -274,7 +279,7 @@ int HGCMService::loadServiceDLL (void)
m_pfnLoad = NULL;
- rc = RTLdrGetSymbol (m_hLdrMod, VBOX_HGCM_SVCLOAD_NAME, (void**)&m_pfnLoad);
+ rc = RTLdrGetSymbol(m_hLdrMod, VBOX_HGCM_SVCLOAD_NAME, (void**)&m_pfnLoad);
if (RT_FAILURE(rc) || !m_pfnLoad)
{
@@ -289,13 +294,13 @@ int HGCMService::loadServiceDLL (void)
if (RT_SUCCESS(rc))
{
- memset (&m_fntable, 0, sizeof (m_fntable));
+ RT_ZERO(m_fntable);
- m_fntable.cbSize = sizeof (m_fntable);
+ m_fntable.cbSize = sizeof(m_fntable);
m_fntable.u32Version = VBOX_HGCM_SVC_VERSION;
m_fntable.pHelpers = &m_svcHelpers;
- rc = m_pfnLoad (&m_fntable);
+ rc = m_pfnLoad(&m_fntable);
LogFlowFunc(("m_pfnLoad rc = %Rrc\n", rc));
@@ -313,7 +318,7 @@ int HGCMService::loadServiceDLL (void)
if (m_fntable.pfnUnload)
{
- m_fntable.pfnUnload (m_fntable.pvService);
+ m_fntable.pfnUnload(m_fntable.pvService);
}
}
}
@@ -328,7 +333,7 @@ int HGCMService::loadServiceDLL (void)
if (RT_FAILURE(rc))
{
- unloadServiceDLL ();
+ unloadServiceDLL();
}
return rc;
@@ -338,14 +343,14 @@ int HGCMService::loadServiceDLL (void)
*
* @return VBox code
*/
-void HGCMService::unloadServiceDLL (void)
+void HGCMService::unloadServiceDLL(void)
{
if (m_hLdrMod)
{
- RTLdrClose (m_hLdrMod);
+ RTLdrClose(m_hLdrMod);
}
- memset (&m_fntable, 0, sizeof (m_fntable));
+ RT_ZERO(m_fntable);
m_pfnLoad = NULL;
m_hLdrMod = NIL_RTLDRMOD;
}
@@ -790,9 +795,9 @@ int HGCMService::instanceCreate (const char *pszServiceLibrary, const char *pszS
/* The maximum length of the thread name, allowed by the RT is 15. */
char szThreadName[16];
- if (!strncmp (pszServiceName, "VBoxShared", 10))
+ if (!strncmp (pszServiceName, RT_STR_TUPLE("VBoxShared")))
RTStrPrintf (szThreadName, sizeof (szThreadName), "Sh%s", pszServiceName + 10);
- else if (!strncmp (pszServiceName, "VBox", 4))
+ else if (!strncmp (pszServiceName, RT_STR_TUPLE("VBox")))
RTStrCopy (szThreadName, sizeof (szThreadName), pszServiceName + 4);
else
RTStrCopy (szThreadName, sizeof (szThreadName), pszServiceName);
diff --git a/src/VBox/Main/src-client/HGCMThread.cpp b/src/VBox/Main/src-client/HGCMThread.cpp
index 49c3db18..d0e046a4 100644
--- a/src/VBox/Main/src-client/HGCMThread.cpp
+++ b/src/VBox/Main/src-client/HGCMThread.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -220,7 +220,7 @@ HGCMThread::HGCMThread ()
m_pFreeTail (NULL),
m_handle (0)
{
- memset (&m_critsect, 0, sizeof (m_critsect));
+ RT_ZERO(m_critsect);
}
HGCMThread::~HGCMThread ()
@@ -306,7 +306,7 @@ int HGCMThread::Initialize (HGCMTHREADHANDLE handle, const char *pszThreadName,
else
{
Log(("hgcmThreadCreate: FAILURE: Can't init a critical section for a hgcm worker thread.\n"));
- memset (&m_critsect, 0, sizeof (m_critsect));
+ RT_ZERO(m_critsect);
}
}
else
diff --git a/src/VBox/Main/src-client/KeyboardImpl.cpp b/src/VBox/Main/src-client/KeyboardImpl.cpp
index 43c2776c..74154dac 100644
--- a/src/VBox/Main/src-client/KeyboardImpl.cpp
+++ b/src/VBox/Main/src-client/KeyboardImpl.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -112,7 +112,7 @@ HRESULT Keyboard::init(Console *aParent)
unconst(mParent) = aParent;
unconst(mEventSource).createObject();
- HRESULT rc = mEventSource->init(static_cast<IKeyboard*>(this));
+ HRESULT rc = mEventSource->init();
AssertComRCReturnRC(rc);
/* Confirm a successful initialization */
@@ -271,6 +271,27 @@ STDMETHODIMP Keyboard::COMGETTER(EventSource)(IEventSource ** aEventSource)
// private methods
//
+DECLCALLBACK(void) Keyboard::keyboardLedStatusChange(PPDMIKEYBOARDCONNECTOR pInterface, PDMKEYBLEDS enmLeds)
+{
+ PDRVMAINKEYBOARD pDrv = PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD(pInterface);
+ pDrv->pKeyboard->getParent()->onKeyboardLedsChange(RT_BOOL(enmLeds & PDMKEYBLEDS_NUMLOCK),
+ RT_BOOL(enmLeds & PDMKEYBLEDS_CAPSLOCK),
+ RT_BOOL(enmLeds & PDMKEYBLEDS_SCROLLLOCK));
+}
+
+/**
+ * @interface_method_impl{PDMIKEYBOARDCONNECTOR,pfnSetActive}
+ */
+DECLCALLBACK(void) Keyboard::keyboardSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
+{
+ PDRVMAINKEYBOARD pDrv = PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD(pInterface);
+ if (fActive)
+ pDrv->u32DevCaps |= KEYBOARD_DEVCAP_ENABLED;
+ else
+ pDrv->u32DevCaps &= ~KEYBOARD_DEVCAP_ENABLED;
+}
+
+
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
@@ -293,55 +314,33 @@ DECLCALLBACK(void *) Keyboard::drvQueryInterface(PPDMIBASE pInterface, const cha
*/
DECLCALLBACK(void) Keyboard::drvDestruct(PPDMDRVINS pDrvIns)
{
- PDRVMAINKEYBOARD pData = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
- LogFlow(("Keyboard::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
+ LogFlow(("Keyboard::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
- if (pData->pKeyboard)
+ if (pThis->pKeyboard)
{
- AutoWriteLock kbdLock(pData->pKeyboard COMMA_LOCKVAL_SRC_POS);
+ AutoWriteLock kbdLock(pThis->pKeyboard COMMA_LOCKVAL_SRC_POS);
for (unsigned cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
- if (pData->pKeyboard->mpDrv[cDev] == pData)
+ if (pThis->pKeyboard->mpDrv[cDev] == pThis)
{
- pData->pKeyboard->mpDrv[cDev] = NULL;
+ pThis->pKeyboard->mpDrv[cDev] = NULL;
break;
}
- pData->pKeyboard->mpVMMDev = NULL;
+ pThis->pKeyboard->mpVMMDev = NULL;
}
}
-DECLCALLBACK(void) keyboardLedStatusChange(PPDMIKEYBOARDCONNECTOR pInterface,
- PDMKEYBLEDS enmLeds)
-{
- PDRVMAINKEYBOARD pDrv = PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD(pInterface);
- pDrv->pKeyboard->getParent()->onKeyboardLedsChange(!!(enmLeds & PDMKEYBLEDS_NUMLOCK),
- !!(enmLeds & PDMKEYBLEDS_CAPSLOCK),
- !!(enmLeds & PDMKEYBLEDS_SCROLLLOCK));
-}
-
-/**
- * @interface_method_impl{PDMIKEYBOARDCONNECTOR,pfnSetActive}
- */
-DECLCALLBACK(void) Keyboard::keyboardSetActive(PPDMIKEYBOARDCONNECTOR pInterface, bool fActive)
-{
- PDRVMAINKEYBOARD pDrv = PPDMIKEYBOARDCONNECTOR_2_MAINKEYBOARD(pInterface);
- if (fActive)
- pDrv->u32DevCaps |= KEYBOARD_DEVCAP_ENABLED;
- else
- pDrv->u32DevCaps &= ~KEYBOARD_DEVCAP_ENABLED;
-}
-
/**
* Construct a keyboard driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
-DECLCALLBACK(int) Keyboard::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg,
- uint32_t fFlags)
+DECLCALLBACK(int) Keyboard::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
- PDRVMAINKEYBOARD pData = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
- LogFlow(("Keyboard::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVMAINKEYBOARD pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINKEYBOARD);
+ LogFlow(("Keyboard::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
/*
* Validate configuration.
@@ -357,14 +356,14 @@ DECLCALLBACK(int) Keyboard::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg,
*/
pDrvIns->IBase.pfnQueryInterface = Keyboard::drvQueryInterface;
- pData->IConnector.pfnLedStatusChange = keyboardLedStatusChange;
- pData->IConnector.pfnSetActive = keyboardSetActive;
+ pThis->IConnector.pfnLedStatusChange = keyboardLedStatusChange;
+ pThis->IConnector.pfnSetActive = Keyboard::keyboardSetActive;
/*
* Get the IKeyboardPort interface of the above driver/device.
*/
- pData->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
- if (!pData->pUpPort)
+ pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIKEYBOARDPORT);
+ if (!pThis->pUpPort)
{
AssertMsgFailed(("Configuration error: No keyboard port interface above!\n"));
return VERR_PDM_MISSING_INTERFACE_ABOVE;
@@ -380,12 +379,12 @@ DECLCALLBACK(int) Keyboard::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg,
AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
return rc;
}
- pData->pKeyboard = (Keyboard *)pv; /** @todo Check this cast! */
+ pThis->pKeyboard = (Keyboard *)pv; /** @todo Check this cast! */
unsigned cDev;
for (cDev = 0; cDev < KEYBOARD_MAX_DEVICES; ++cDev)
- if (!pData->pKeyboard->mpDrv[cDev])
+ if (!pThis->pKeyboard->mpDrv[cDev])
{
- pData->pKeyboard->mpDrv[cDev] = pData;
+ pThis->pKeyboard->mpDrv[cDev] = pThis;
break;
}
if (cDev == KEYBOARD_MAX_DEVICES)
diff --git a/src/VBox/Main/src-client/MachineDebuggerImpl.cpp b/src/VBox/Main/src-client/MachineDebuggerImpl.cpp
index 11dd820a..1520bb55 100644
--- a/src/VBox/Main/src-client/MachineDebuggerImpl.cpp
+++ b/src/VBox/Main/src-client/MachineDebuggerImpl.cpp
@@ -1,10 +1,10 @@
/* $Id: MachineDebuggerImpl.cpp $ */
/** @file
- * VBox IMachineDebugger COM class implementation.
+ * VBox IMachineDebugger COM class implementation (VBoxC).
*/
/*
- * Copyright (C) 2006-2010 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;
@@ -15,6 +15,9 @@
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
#include "MachineDebuggerImpl.h"
#include "Global.h"
@@ -26,19 +29,12 @@
#include <VBox/vmm/em.h>
#include <VBox/vmm/patm.h>
#include <VBox/vmm/csam.h>
-#include <VBox/vmm/vm.h>
+#include <VBox/vmm/uvm.h>
#include <VBox/vmm/tm.h>
-#include <VBox/vmm/hwaccm.h>
+#include <VBox/vmm/hm.h>
#include <VBox/err.h>
#include <iprt/cpp/utils.h>
-// defines
-/////////////////////////////////////////////////////////////////////////////
-
-
-// globals
-/////////////////////////////////////////////////////////////////////////////
-
// constructor / destructor
/////////////////////////////////////////////////////////////////////////////
@@ -85,6 +81,8 @@ HRESULT MachineDebugger::init (Console *aParent)
unconst(mParent) = aParent;
+ for (unsigned i = 0; i < RT_ELEMENTS(maiQueuedEmExecPolicyParams); i++)
+ maiQueuedEmExecPolicyParams[i] = UINT8_MAX;
mSingleStepQueued = ~0;
mRecompileUserQueued = ~0;
mRecompileSupervisorQueued = ~0;
@@ -171,54 +169,59 @@ STDMETHODIMP MachineDebugger::COMSETTER(SingleStep)(BOOL a_fEnable)
}
/**
- * Returns the current recompile user mode code flag.
+ * Internal worker for getting an EM executable policy setting.
*
- * @returns COM status code
- * @param a_fEnabled address of result variable
+ * @returns COM status code.
+ * @param enmPolicy Which EM policy.
+ * @param pfEnforced Where to return the policy setting.
*/
-STDMETHODIMP MachineDebugger::COMGETTER(RecompileUser) (BOOL *aEnabled)
+HRESULT MachineDebugger::getEmExecPolicyProperty(EMEXECPOLICY enmPolicy, BOOL *pfEnforced)
{
- CheckComArgOutPointerValid(aEnabled);
+ CheckComArgOutPointerValid(pfEnforced);
AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
- AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
- Console::SafeVMPtrQuiet pVM (mParent);
-
- if (pVM.isOk())
- *aEnabled = !EMIsRawRing3Enabled (pVM.raw());
- else
- *aEnabled = false;
-
- return S_OK;
+ HRESULT hrc = autoCaller.rc();
+ if (SUCCEEDED(hrc))
+ {
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (queueSettings())
+ *pfEnforced = maiQueuedEmExecPolicyParams[enmPolicy] == 1;
+ else
+ {
+ bool fEnforced = false;
+ Console::SafeVMPtrQuiet ptrVM(mParent);
+ hrc = ptrVM.rc();
+ if (SUCCEEDED(hrc))
+ EMR3QueryExecutionPolicy(ptrVM.rawUVM(), enmPolicy, &fEnforced);
+ *pfEnforced = fEnforced;
+ }
+ }
+ return hrc;
}
/**
- * Sets the recompile user mode code flag.
+ * Internal worker for setting an EM executable policy.
*
- * @returns COM status
- * @param aEnable new user mode code recompile flag.
+ * @returns COM status code.
+ * @param enmPolicy Which policy to change.
+ * @param fEnforce Whether to enforce the policy or not.
*/
-STDMETHODIMP MachineDebugger::COMSETTER(RecompileUser)(BOOL aEnable)
+HRESULT MachineDebugger::setEmExecPolicyProperty(EMEXECPOLICY enmPolicy, BOOL fEnforce)
{
- LogFlowThisFunc(("enable=%d\n", aEnable));
-
AutoCaller autoCaller(this);
HRESULT hrc = autoCaller.rc();
if (SUCCEEDED(hrc))
{
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
if (queueSettings())
- mRecompileUserQueued = aEnable; // queue the request
+ maiQueuedEmExecPolicyParams[enmPolicy] = fEnforce ? 1 : 0;
else
{
- Console::SafeVMPtr ptrVM(mParent);
+ Console::SafeVMPtrQuiet ptrVM(mParent);
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
- int vrc = EMR3SetExecutionPolicy(ptrVM.raw(), EMEXECPOLICY_RECOMPILE_RING3, RT_BOOL(aEnable));
+ int vrc = EMR3SetExecutionPolicy(ptrVM.rawUVM(), enmPolicy, fEnforce != FALSE);
if (RT_FAILURE(vrc))
hrc = setError(VBOX_E_VM_ERROR, tr("EMR3SetExecutionPolicy failed with %Rrc"), vrc);
}
@@ -228,6 +231,29 @@ STDMETHODIMP MachineDebugger::COMSETTER(RecompileUser)(BOOL aEnable)
}
/**
+ * Returns the current recompile user mode code flag.
+ *
+ * @returns COM status code
+ * @param a_fEnabled address of result variable
+ */
+STDMETHODIMP MachineDebugger::COMGETTER(RecompileUser) (BOOL *aEnabled)
+{
+ return getEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING3, aEnabled);
+}
+
+/**
+ * Sets the recompile user mode code flag.
+ *
+ * @returns COM status
+ * @param aEnable new user mode code recompile flag.
+ */
+STDMETHODIMP MachineDebugger::COMSETTER(RecompileUser)(BOOL aEnable)
+{
+ LogFlowThisFunc(("enable=%d\n", aEnable));
+ return setEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING3, aEnable);
+}
+
+/**
* Returns the current recompile supervisor code flag.
*
* @returns COM status code
@@ -235,21 +261,7 @@ STDMETHODIMP MachineDebugger::COMSETTER(RecompileUser)(BOOL aEnable)
*/
STDMETHODIMP MachineDebugger::COMGETTER(RecompileSupervisor) (BOOL *aEnabled)
{
- CheckComArgOutPointerValid(aEnabled);
-
- AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
- AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
- Console::SafeVMPtrQuiet pVM (mParent);
-
- if (pVM.isOk())
- *aEnabled = !EMIsRawRing0Enabled (pVM.raw());
- else
- *aEnabled = false;
-
- return S_OK;
+ return getEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING0, aEnabled);
}
/**
@@ -261,27 +273,30 @@ STDMETHODIMP MachineDebugger::COMGETTER(RecompileSupervisor) (BOOL *aEnabled)
STDMETHODIMP MachineDebugger::COMSETTER(RecompileSupervisor)(BOOL aEnable)
{
LogFlowThisFunc(("enable=%d\n", aEnable));
+ return setEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING0, aEnable);
+}
- AutoCaller autoCaller(this);
- HRESULT hrc = autoCaller.rc();
- if (SUCCEEDED(hrc))
- {
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- if (queueSettings())
- mRecompileSupervisorQueued = aEnable; // queue the request
- else
- {
- Console::SafeVMPtr ptrVM(mParent);
- hrc = ptrVM.rc();
- if (SUCCEEDED(hrc))
- {
- int vrc = EMR3SetExecutionPolicy(ptrVM.raw(), EMEXECPOLICY_RECOMPILE_RING0, RT_BOOL(aEnable));
- if (RT_FAILURE(vrc))
- hrc = setError(VBOX_E_VM_ERROR, tr("EMR3SetExecutionPolicy failed with %Rrc"), vrc);
- }
- }
- }
- return hrc;
+/**
+ * Returns the current execute-all-in-IEM setting.
+ *
+ * @returns COM status code
+ * @param aEnabled Address of result variable.
+ */
+STDMETHODIMP MachineDebugger::COMGETTER(ExecuteAllInIEM) (BOOL *aEnabled)
+{
+ return getEmExecPolicyProperty(EMEXECPOLICY_IEM_ALL, aEnabled);
+}
+
+/**
+ * Changes the execute-all-in-IEM setting.
+ *
+ * @returns COM status code
+ * @param aEnable New setting.
+ */
+STDMETHODIMP MachineDebugger::COMSETTER(ExecuteAllInIEM)(BOOL aEnable)
+{
+ LogFlowThisFunc(("enable=%d\n", aEnable));
+ return setEmExecPolicyProperty(EMEXECPOLICY_IEM_ALL, aEnable);
}
/**
@@ -295,15 +310,17 @@ STDMETHODIMP MachineDebugger::COMGETTER(PATMEnabled) (BOOL *aEnabled)
CheckComArgOutPointerValid(aEnabled);
AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
+ if (FAILED(autoCaller.rc()))
+ return autoCaller.rc();
+#ifdef VBOX_WITH_RAW_MODE
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- Console::SafeVMPtrQuiet pVM (mParent);
-
- if (pVM.isOk())
- *aEnabled = PATMIsEnabled (pVM.raw());
+ Console::SafeVMPtrQuiet ptrVM(mParent);
+ if (ptrVM.isOk())
+ *aEnabled = PATMR3IsEnabled (ptrVM.rawUVM());
else
+#endif
*aEnabled = false;
return S_OK;
@@ -322,6 +339,7 @@ STDMETHODIMP MachineDebugger::COMSETTER(PATMEnabled) (BOOL aEnable)
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
+#ifdef VBOX_WITH_RAW_MODE
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (queueSettings())
@@ -331,11 +349,18 @@ STDMETHODIMP MachineDebugger::COMSETTER(PATMEnabled) (BOOL aEnable)
return S_OK;
}
- Console::SafeVMPtr pVM(mParent);
- if (FAILED(pVM.rc())) return pVM.rc();
+ Console::SafeVMPtr ptrVM(mParent);
+ if (FAILED(ptrVM.rc()))
+ return ptrVM.rc();
- PATMR3AllowPatching (pVM, aEnable);
+ int vrc = PATMR3AllowPatching(ptrVM.rawUVM(), RT_BOOL(aEnable));
+ if (RT_FAILURE(vrc))
+ return setError(VBOX_E_VM_ERROR, tr("PATMR3AllowPatching returned %Rrc"), vrc);
+#else /* !VBOX_WITH_RAW_MODE */
+ if (aEnable)
+ return setError(VBOX_E_VM_ERROR, tr("PATM not present"), VERR_NOT_SUPPORTED);
+#endif /* !VBOX_WITH_RAW_MODE */
return S_OK;
}
@@ -352,13 +377,15 @@ STDMETHODIMP MachineDebugger::COMGETTER(CSAMEnabled) (BOOL *aEnabled)
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
+#ifdef VBOX_WITH_RAW_MODE
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- Console::SafeVMPtrQuiet pVM (mParent);
+ Console::SafeVMPtrQuiet ptrVM(mParent);
- if (pVM.isOk())
- *aEnabled = CSAMIsEnabled (pVM.raw());
+ if (ptrVM.isOk())
+ *aEnabled = CSAMR3IsEnabled(ptrVM.rawUVM());
else
+#endif /* VBOX_WITH_RAW_MODE */
*aEnabled = false;
return S_OK;
@@ -377,6 +404,7 @@ STDMETHODIMP MachineDebugger::COMSETTER(CSAMEnabled) (BOOL aEnable)
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
+#ifdef VBOX_WITH_RAW_MODE
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (queueSettings())
@@ -386,20 +414,18 @@ STDMETHODIMP MachineDebugger::COMSETTER(CSAMEnabled) (BOOL aEnable)
return S_OK;
}
- Console::SafeVMPtr pVM(mParent);
- if (FAILED(pVM.rc())) return pVM.rc();
-
- int vrc;
- if (aEnable)
- vrc = CSAMEnableScanning (pVM);
- else
- vrc = CSAMDisableScanning (pVM);
+ Console::SafeVMPtr ptrVM(mParent);
+ if (FAILED(ptrVM.rc()))
+ return ptrVM.rc();
+ int vrc = CSAMR3SetScanningEnabled(ptrVM.rawUVM(), aEnable != FALSE);
if (RT_FAILURE(vrc))
- {
- /** @todo handle error case */
- }
+ return setError(VBOX_E_VM_ERROR, tr("CSAMR3SetScanningEnabled returned %Rrc"), vrc);
+#else /* !VBOX_WITH_RAW_MODE */
+ if (aEnable)
+ return setError(VBOX_E_VM_ERROR, tr("CASM not present"), VERR_NOT_SUPPORTED);
+#endif /* !VBOX_WITH_RAW_MODE */
return S_OK;
}
@@ -450,11 +476,11 @@ STDMETHODIMP MachineDebugger::COMSETTER(LogEnabled) (BOOL aEnabled)
return S_OK;
}
- Console::SafeVMPtr pVM(mParent);
- if (FAILED(pVM.rc())) return pVM.rc();
+ Console::SafeVMPtr ptrVM(mParent);
+ if (FAILED(ptrVM.rc())) return ptrVM.rc();
#ifdef LOG_ENABLED
- int vrc = DBGFR3LogModifyFlags (pVM, aEnabled ? "enabled" : "disabled");
+ int vrc = DBGFR3LogModifyFlags(ptrVM.rawUVM(), aEnabled ? "enabled" : "disabled");
if (RT_FAILURE(vrc))
{
/** @todo handle error code. */
@@ -603,10 +629,10 @@ STDMETHODIMP MachineDebugger::COMGETTER(HWVirtExEnabled) (BOOL *aEnabled)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- Console::SafeVMPtrQuiet pVM (mParent);
+ Console::SafeVMPtrQuiet ptrVM(mParent);
- if (pVM.isOk())
- *aEnabled = HWACCMIsEnabled (pVM.raw());
+ if (ptrVM.isOk())
+ *aEnabled = HMR3IsEnabled(ptrVM.rawUVM());
else
*aEnabled = false;
@@ -619,19 +645,20 @@ STDMETHODIMP MachineDebugger::COMGETTER(HWVirtExEnabled) (BOOL *aEnabled)
* @returns COM status code
* @param aEnabled address of result variable
*/
-STDMETHODIMP MachineDebugger::COMGETTER(HWVirtExNestedPagingEnabled) (BOOL *aEnabled)
+STDMETHODIMP MachineDebugger::COMGETTER(HWVirtExNestedPagingEnabled)(BOOL *aEnabled)
{
CheckComArgOutPointerValid(aEnabled);
AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
+ if (FAILED(autoCaller.rc()))
+ return autoCaller.rc();
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- Console::SafeVMPtrQuiet pVM (mParent);
+ Console::SafeVMPtrQuiet ptrVM(mParent);
- if (pVM.isOk())
- *aEnabled = HWACCMR3IsNestedPagingActive (pVM.raw());
+ if (ptrVM.isOk())
+ *aEnabled = HMR3IsNestedPagingActive(ptrVM.rawUVM());
else
*aEnabled = false;
@@ -649,14 +676,41 @@ STDMETHODIMP MachineDebugger::COMGETTER(HWVirtExVPIDEnabled) (BOOL *aEnabled)
CheckComArgOutPointerValid(aEnabled);
AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
+ if (FAILED(autoCaller.rc()))
+ return autoCaller.rc();
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- Console::SafeVMPtrQuiet pVM (mParent);
+ Console::SafeVMPtrQuiet ptrVM(mParent);
- if (pVM.isOk())
- *aEnabled = HWACCMR3IsVPIDActive (pVM.raw());
+ if (ptrVM.isOk())
+ *aEnabled = HMR3IsVpidActive(ptrVM.rawUVM());
+ else
+ *aEnabled = false;
+
+ return S_OK;
+}
+
+/**
+ * Returns the current unrestricted execution setting.
+ *
+ * @returns COM status code
+ * @param aEnabled address of result variable
+ */
+STDMETHODIMP MachineDebugger::COMGETTER(HWVirtExUXEnabled) (BOOL *aEnabled)
+{
+ CheckComArgOutPointerValid(aEnabled);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc()))
+ return autoCaller.rc();
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Console::SafeVMPtrQuiet ptrVM(mParent);
+
+ if (ptrVM.isOk())
+ *aEnabled = HMR3IsUXActive(ptrVM.rawUVM());
else
*aEnabled = false;
@@ -680,7 +734,7 @@ STDMETHODIMP MachineDebugger::COMGETTER(OSName)(BSTR *a_pbstrName)
* Do the job and try convert the name.
*/
char szName[64];
- int vrc = DBGFR3OSQueryNameAndVersion(ptrVM.raw(), szName, sizeof(szName), NULL, 0);
+ int vrc = DBGFR3OSQueryNameAndVersion(ptrVM.rawUVM(), szName, sizeof(szName), NULL, 0);
if (RT_SUCCESS(vrc))
{
try
@@ -717,7 +771,7 @@ STDMETHODIMP MachineDebugger::COMGETTER(OSVersion)(BSTR *a_pbstrVersion)
* Do the job and try convert the name.
*/
char szVersion[256];
- int vrc = DBGFR3OSQueryNameAndVersion(ptrVM.raw(), NULL, 0, szVersion, sizeof(szVersion));
+ int vrc = DBGFR3OSQueryNameAndVersion(ptrVM.rawUVM(), NULL, 0, szVersion, sizeof(szVersion));
if (RT_SUCCESS(vrc))
{
try
@@ -752,12 +806,13 @@ STDMETHODIMP MachineDebugger::COMGETTER(PAEEnabled) (BOOL *aEnabled)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- Console::SafeVMPtrQuiet pVM (mParent);
+ Console::SafeVMPtrQuiet ptrVM(mParent);
- if (pVM.isOk())
+ if (ptrVM.isOk())
{
- uint64_t cr4 = CPUMGetGuestCR4 (VMMGetCpu0(pVM.raw()));
- *aEnabled = !!(cr4 & X86_CR4_PAE);
+ uint32_t cr4;
+ int rc = DBGFR3RegCpuQueryU32(ptrVM.rawUVM(), 0 /*idCpu*/, DBGFREG_CR4, &cr4); AssertRC(rc);
+ *aEnabled = RT_BOOL(cr4 & X86_CR4_PAE);
}
else
*aEnabled = false;
@@ -784,7 +839,7 @@ STDMETHODIMP MachineDebugger::COMGETTER(VirtualTimeRate)(ULONG *a_puPct)
Console::SafeVMPtr ptrVM(mParent);
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
- *a_puPct = TMGetWarpDrive(ptrVM.raw());
+ *a_puPct = TMR3GetWarpDrive(ptrVM.rawUVM());
}
return hrc;
@@ -814,7 +869,7 @@ STDMETHODIMP MachineDebugger::COMSETTER(VirtualTimeRate)(ULONG a_uPct)
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
- int vrc = TMR3SetWarpDrive(ptrVM.raw(), a_uPct);
+ int vrc = TMR3SetWarpDrive(ptrVM.rawUVM(), a_uPct);
if (RT_FAILURE(vrc))
hrc = setError(VBOX_E_VM_ERROR, tr("TMR3SetWarpDrive(, %u) failed with rc=%Rrc"), a_uPct, vrc);
}
@@ -825,7 +880,7 @@ STDMETHODIMP MachineDebugger::COMSETTER(VirtualTimeRate)(ULONG a_uPct)
}
/**
- * Hack for getting the VM handle.
+ * Hack for getting the user mode VM handle (UVM).
*
* This is only temporary (promise) while prototyping the debugger.
*
@@ -833,10 +888,12 @@ STDMETHODIMP MachineDebugger::COMSETTER(VirtualTimeRate)(ULONG a_uPct)
* @param a_u64Vm Where to store the vm handle. Since there is no
* uintptr_t in COM, we're using the max integer.
* (No, ULONG is not pointer sized!)
+ * @remarks The returned handle must be passed to VMR3ReleaseUVM()!
+ * @remarks Prior to 4.3 this returned PVM.
*/
-STDMETHODIMP MachineDebugger::COMGETTER(VM)(LONG64 *a_u64Vm)
+STDMETHODIMP MachineDebugger::COMGETTER(VM)(LONG64 *a_i64Vm)
{
- CheckComArgOutPointerValid(a_u64Vm);
+ CheckComArgOutPointerValid(a_i64Vm);
AutoCaller autoCaller(this);
HRESULT hrc = autoCaller.rc();
@@ -847,10 +904,13 @@ STDMETHODIMP MachineDebugger::COMGETTER(VM)(LONG64 *a_u64Vm)
Console::SafeVMPtr ptrVM(mParent);
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
- *a_u64Vm = (intptr_t)ptrVM.raw();
+ {
+ VMR3RetainUVM(ptrVM.rawUVM());
+ *a_i64Vm = (intptr_t)ptrVM.rawUVM();
+ }
/*
- * Note! pVM protection provided by SafeVMPtr is no long effective
+ * Note! ptrVM protection provided by SafeVMPtr is no long effective
* after we return from this method.
*/
}
@@ -877,7 +937,7 @@ STDMETHODIMP MachineDebugger::DumpGuestCore(IN_BSTR a_bstrFilename, IN_BSTR a_bs
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
- int vrc = DBGFR3CoreWrite(ptrVM, strFilename.c_str(), false /*fReplaceFile*/);
+ int vrc = DBGFR3CoreWrite(ptrVM.rawUVM(), strFilename.c_str(), false /*fReplaceFile*/);
if (RT_SUCCESS(vrc))
hrc = S_OK;
else
@@ -1037,7 +1097,7 @@ STDMETHODIMP MachineDebugger::Info(IN_BSTR a_bstrName, IN_BSTR a_bstrArgs, BSTR
*/
MACHINEDEBUGGERINOFHLP Hlp;
MachineDebuggerInfoInit(&Hlp);
- int vrc = DBGFR3Info(ptrVM.raw(), strName.c_str(), strArgs.c_str(), &Hlp.Core);
+ int vrc = DBGFR3Info(ptrVM.rawUVM(), strName.c_str(), strArgs.c_str(), &Hlp.Core);
if (RT_SUCCESS(vrc))
{
if (!Hlp.fOutOfMemory)
@@ -1079,11 +1139,11 @@ STDMETHODIMP MachineDebugger::InjectNMI()
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
- int vrc = HWACCMR3InjectNMI(ptrVM);
+ int vrc = DBGFR3InjectNMI(ptrVM.rawUVM(), 0);
if (RT_SUCCESS(vrc))
hrc = S_OK;
else
- hrc = setError(E_FAIL, tr("HWACCMR3InjectNMI failed with %Rrc"), vrc);
+ hrc = setError(E_FAIL, tr("DBGFR3InjectNMI failed with %Rrc"), vrc);
}
}
return hrc;
@@ -1104,7 +1164,7 @@ STDMETHODIMP MachineDebugger::ModifyLogFlags(IN_BSTR a_bstrSettings)
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
- int vrc = DBGFR3LogModifyFlags(ptrVM, strSettings.c_str());
+ int vrc = DBGFR3LogModifyFlags(ptrVM.rawUVM(), strSettings.c_str());
if (RT_SUCCESS(vrc))
hrc = S_OK;
else
@@ -1129,7 +1189,7 @@ STDMETHODIMP MachineDebugger::ModifyLogGroups(IN_BSTR a_bstrSettings)
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
- int vrc = DBGFR3LogModifyGroups(ptrVM, strSettings.c_str());
+ int vrc = DBGFR3LogModifyGroups(ptrVM.rawUVM(), strSettings.c_str());
if (RT_SUCCESS(vrc))
hrc = S_OK;
else
@@ -1154,7 +1214,7 @@ STDMETHODIMP MachineDebugger::ModifyLogDestinations(IN_BSTR a_bstrSettings)
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
- int vrc = DBGFR3LogModifyDestinations(ptrVM, strSettings.c_str());
+ int vrc = DBGFR3LogModifyDestinations(ptrVM.rawUVM(), strSettings.c_str());
if (RT_SUCCESS(vrc))
hrc = S_OK;
else
@@ -1206,7 +1266,7 @@ STDMETHODIMP MachineDebugger::DetectOS(BSTR *a_pbstrName)
*/
/** @todo automatically load the DBGC plugins or this is a waste of time. */
char szName[64];
- int vrc = DBGFR3OSDetect(ptrVM.raw(), szName, sizeof(szName));
+ int vrc = DBGFR3OSDetect(ptrVM.rawUVM(), szName, sizeof(szName));
if (RT_SUCCESS(vrc) && vrc != VINF_DBGF_OS_NOT_DETCTED)
{
try
@@ -1281,7 +1341,7 @@ STDMETHODIMP MachineDebugger::GetRegister(ULONG a_idCpu, IN_BSTR a_bstrName, BST
*/
DBGFREGVAL Value;
DBGFREGVALTYPE enmType;
- int vrc = DBGFR3RegNmQuery(ptrVM.raw(), a_idCpu, strName.c_str(), &Value, &enmType);
+ int vrc = DBGFR3RegNmQuery(ptrVM.rawUVM(), a_idCpu, strName.c_str(), &Value, &enmType);
if (RT_SUCCESS(vrc))
{
try
@@ -1329,13 +1389,13 @@ STDMETHODIMP MachineDebugger::GetRegisters(ULONG a_idCpu, ComSafeArrayOut(BSTR,
* Real work.
*/
size_t cRegs;
- int vrc = DBGFR3RegNmQueryAllCount(ptrVM.raw(), &cRegs);
+ int vrc = DBGFR3RegNmQueryAllCount(ptrVM.rawUVM(), &cRegs);
if (RT_SUCCESS(vrc))
{
PDBGFREGENTRYNM paRegs = (PDBGFREGENTRYNM)RTMemAllocZ(sizeof(paRegs[0]) * cRegs);
if (paRegs)
{
- vrc = DBGFR3RegNmQueryAll(ptrVM.raw(), paRegs, cRegs);
+ vrc = DBGFR3RegNmQueryAll(ptrVM.rawUVM(), paRegs, cRegs);
if (RT_SUCCESS(vrc))
{
try
@@ -1345,7 +1405,6 @@ STDMETHODIMP MachineDebugger::GetRegisters(ULONG a_idCpu, ComSafeArrayOut(BSTR,
for (uint32_t iReg = 0; iReg < cRegs; iReg++)
{
- char szHex[128];
Bstr bstrValue;
hrc = formatRegisterValue(&bstrValue, &paRegs[iReg].Val, paRegs[iReg].enmType);
@@ -1402,12 +1461,12 @@ STDMETHODIMP MachineDebugger::DumpGuestStack(ULONG a_idCpu, BSTR *a_pbstrStack)
*/
STDMETHODIMP MachineDebugger::ResetStats(IN_BSTR aPattern)
{
- Console::SafeVMPtrQuiet pVM (mParent);
+ Console::SafeVMPtrQuiet ptrVM(mParent);
- if (!pVM.isOk())
+ if (!ptrVM.isOk())
return setError(VBOX_E_INVALID_VM_STATE, "Machine is not running");
- STAMR3Reset(pVM, Utf8Str(aPattern).c_str());
+ STAMR3Reset(ptrVM.rawUVM(), Utf8Str(aPattern).c_str());
return S_OK;
}
@@ -1418,14 +1477,14 @@ STDMETHODIMP MachineDebugger::ResetStats(IN_BSTR aPattern)
* @returns COM status code.
* @param aPattern The selection pattern. A bit similar to filename globbing.
*/
-STDMETHODIMP MachineDebugger::DumpStats (IN_BSTR aPattern)
+STDMETHODIMP MachineDebugger::DumpStats(IN_BSTR aPattern)
{
- Console::SafeVMPtrQuiet pVM (mParent);
+ Console::SafeVMPtrQuiet ptrVM(mParent);
- if (!pVM.isOk())
+ if (!ptrVM.isOk())
return setError(VBOX_E_INVALID_VM_STATE, "Machine is not running");
- STAMR3Dump(pVM, Utf8Str(aPattern).c_str());
+ STAMR3Dump(ptrVM.rawUVM(), Utf8Str(aPattern).c_str());
return S_OK;
}
@@ -1440,13 +1499,13 @@ STDMETHODIMP MachineDebugger::DumpStats (IN_BSTR aPattern)
*/
STDMETHODIMP MachineDebugger::GetStats (IN_BSTR aPattern, BOOL aWithDescriptions, BSTR *aStats)
{
- Console::SafeVMPtrQuiet pVM (mParent);
+ Console::SafeVMPtrQuiet ptrVM (mParent);
- if (!pVM.isOk())
+ if (!ptrVM.isOk())
return setError(VBOX_E_INVALID_VM_STATE, "Machine is not running");
char *pszSnapshot;
- int vrc = STAMR3Snapshot(pVM, Utf8Str(aPattern).c_str(), &pszSnapshot, NULL,
+ int vrc = STAMR3Snapshot(ptrVM.rawUVM(), Utf8Str(aPattern).c_str(), &pszSnapshot, NULL,
!!aWithDescriptions);
if (RT_FAILURE(vrc))
return vrc == VERR_NO_MEMORY ? E_OUTOFMEMORY : E_FAIL;
@@ -1456,6 +1515,7 @@ STDMETHODIMP MachineDebugger::GetStats (IN_BSTR aPattern, BOOL aWithDescriptions
* Until that's done, this method is kind of useless for debugger statistics GUI because
* of the amount statistics in a debug build. */
Bstr(pszSnapshot).detachTo(aStats);
+ STAMR3SnapshotFree(ptrVM.rawUVM(), pszSnapshot);
return S_OK;
}
@@ -1472,16 +1532,12 @@ void MachineDebugger::flushQueuedSettings()
COMSETTER(SingleStep)(mSingleStepQueued);
mSingleStepQueued = ~0;
}
- if (mRecompileUserQueued != ~0)
- {
- COMSETTER(RecompileUser)(mRecompileUserQueued);
- mRecompileUserQueued = ~0;
- }
- if (mRecompileSupervisorQueued != ~0)
- {
- COMSETTER(RecompileSupervisor)(mRecompileSupervisorQueued);
- mRecompileSupervisorQueued = ~0;
- }
+ for (unsigned i = 0; i < EMEXECPOLICY_END; i++)
+ if (maiQueuedEmExecPolicyParams[i] != UINT8_MAX)
+ {
+ setEmExecPolicyProperty((EMEXECPOLICY)i, RT_BOOL(maiQueuedEmExecPolicyParams[i]));
+ maiQueuedEmExecPolicyParams[i] = UINT8_MAX;
+ }
if (mPatmEnabledQueued != ~0)
{
COMSETTER(PATMEnabled)(mPatmEnabledQueued);
diff --git a/src/VBox/Main/src-client/MouseImpl.cpp b/src/VBox/Main/src-client/MouseImpl.cpp
index 641f1ad8..652f2df3 100644
--- a/src/VBox/Main/src-client/MouseImpl.cpp
+++ b/src/VBox/Main/src-client/MouseImpl.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2008 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -36,7 +36,9 @@ enum
/** The mouse device can do relative reporting */
MOUSE_DEVCAP_RELATIVE = 1,
/** The mouse device can do absolute reporting */
- MOUSE_DEVCAP_ABSOLUTE = 2
+ MOUSE_DEVCAP_ABSOLUTE = 2,
+ /** The mouse device can do absolute reporting */
+ MOUSE_DEVCAP_MULTI_TOUCH = 4
};
/** @} */
@@ -75,8 +77,8 @@ Mouse::~Mouse()
HRESULT Mouse::FinalConstruct()
{
RT_ZERO(mpDrv);
- mcLastAbsX = 0x8000;
- mcLastAbsY = 0x8000;
+ mcLastX = 0x8000;
+ mcLastY = 0x8000;
mfLastButtons = 0;
mfVMMDevGuestCaps = 0;
return BaseFinalConstruct();
@@ -97,7 +99,7 @@ void Mouse::FinalRelease()
* @returns COM result indicator
* @param parent handle of our parent object
*/
-HRESULT Mouse::init (Console *parent)
+HRESULT Mouse::init (ConsoleMouseInterface *parent)
{
LogFlowThisFunc(("\n"));
@@ -109,13 +111,11 @@ HRESULT Mouse::init (Console *parent)
unconst(mParent) = parent;
-#ifndef VBOXBFE_WITHOUT_COM
unconst(mEventSource).createObject();
- HRESULT rc = mEventSource->init(static_cast<IMouse*>(this));
+ HRESULT rc = mEventSource->init();
AssertComRCReturnRC(rc);
mMouseEvent.init(mEventSource, VBoxEventType_OnGuestMouse,
- 0, 0, 0, 0, 0);
-#endif
+ 0, 0, 0, 0, 0, 0);
/* Confirm a successful initialization */
autoInitSpan.setSucceeded();
@@ -143,13 +143,9 @@ void Mouse::uninit()
mpDrv[i] = NULL;
}
-#ifdef VBOXBFE_WITHOUT_COM
- mParent = NULL;
-#else
mMouseEvent.uninit();
unconst(mEventSource).setNull();
unconst(mParent) = NULL;
-#endif
}
@@ -162,7 +158,7 @@ void Mouse::uninit()
HRESULT Mouse::updateVMMDevMouseCaps(uint32_t fCapsAdded,
uint32_t fCapsRemoved)
{
- VMMDev *pVMMDev = mParent->getVMMDev();
+ VMMDevMouseInterface *pVMMDev = mParent->getVMMDevMouseInterface();
if (!pVMMDev)
return E_FAIL; /* No assertion, as the front-ends can send events
* at all sorts of inconvenient times. */
@@ -176,9 +172,8 @@ HRESULT Mouse::updateVMMDevMouseCaps(uint32_t fCapsAdded,
}
/**
- * Returns whether the current setup can accept absolute mouse events, either
- * because an emulated absolute pointing device is active or because the Guest
- * Additions are.
+ * Returns whether the currently active device portfolio can accept absolute
+ * mouse events.
*
* @returns COM status code
* @param absoluteSupported address of result variable
@@ -196,8 +191,8 @@ STDMETHODIMP Mouse::COMGETTER(AbsoluteSupported) (BOOL *absoluteSupported)
}
/**
- * Returns whether the current setup can accept relative mouse events, that is,
- * whether an emulated relative pointing device is active.
+ * Returns whether the currently active device portfolio can accept relative
+ * mouse events.
*
* @returns COM status code
* @param relativeSupported address of result variable
@@ -215,6 +210,25 @@ STDMETHODIMP Mouse::COMGETTER(RelativeSupported) (BOOL *relativeSupported)
}
/**
+ * Returns whether the currently active device portfolio can accept multi-touch
+ * mouse events.
+ *
+ * @returns COM status code
+ * @param multiTouchSupported address of result variable
+ */
+STDMETHODIMP Mouse::COMGETTER(MultiTouchSupported) (BOOL *multiTouchSupported)
+{
+ if (!multiTouchSupported)
+ return E_POINTER;
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ *multiTouchSupported = supportsMT();
+ return S_OK;
+}
+
+/**
* Returns whether the guest can currently switch to drawing the mouse cursor
* itself if it is asked to by the front-end.
*
@@ -255,7 +269,6 @@ static uint32_t mouseButtonsToPDM(LONG buttonState)
return fButtons;
}
-#ifndef VBOXBFE_WITHOUT_COM
STDMETHODIMP Mouse::COMGETTER(EventSource)(IEventSource ** aEventSource)
{
CheckComArgOutPointerValid(aEventSource);
@@ -268,7 +281,6 @@ STDMETHODIMP Mouse::COMGETTER(EventSource)(IEventSource ** aEventSource)
return S_OK;
}
-#endif
/**
* Send a relative pointer event to the relative device we deem most
@@ -312,16 +324,16 @@ HRESULT Mouse::reportRelEventToMouseDev(int32_t dx, int32_t dy, int32_t dz,
*
* @returns COM status code
*/
-HRESULT Mouse::reportAbsEventToMouseDev(int32_t mouseXAbs, int32_t mouseYAbs,
+HRESULT Mouse::reportAbsEventToMouseDev(int32_t x, int32_t y,
int32_t dz, int32_t dw, uint32_t fButtons)
{
- if ( mouseXAbs < VMMDEV_MOUSE_RANGE_MIN
- || mouseXAbs > VMMDEV_MOUSE_RANGE_MAX)
+ if ( x < VMMDEV_MOUSE_RANGE_MIN
+ || x > VMMDEV_MOUSE_RANGE_MAX)
return S_OK;
- if ( mouseYAbs < VMMDEV_MOUSE_RANGE_MIN
- || mouseYAbs > VMMDEV_MOUSE_RANGE_MAX)
+ if ( y < VMMDEV_MOUSE_RANGE_MIN
+ || y > VMMDEV_MOUSE_RANGE_MAX)
return S_OK;
- if ( mouseXAbs != mcLastAbsX || mouseYAbs != mcLastAbsY
+ if ( x != mcLastX || y != mcLastY
|| dz || dw || fButtons != mfLastButtons)
{
PPDMIMOUSEPORT pUpPort = NULL;
@@ -337,7 +349,7 @@ HRESULT Mouse::reportAbsEventToMouseDev(int32_t mouseXAbs, int32_t mouseYAbs,
if (!pUpPort)
return S_OK;
- int vrc = pUpPort->pfnPutEventAbs(pUpPort, mouseXAbs, mouseYAbs, dz,
+ int vrc = pUpPort->pfnPutEventAbs(pUpPort, x, y, dz,
dw, fButtons);
if (RT_FAILURE(vrc))
return setError(VBOX_E_IPRT_ERROR,
@@ -349,6 +361,44 @@ HRESULT Mouse::reportAbsEventToMouseDev(int32_t mouseXAbs, int32_t mouseYAbs,
return S_OK;
}
+HRESULT Mouse::reportMultiTouchEventToDevice(uint8_t cContacts,
+ const uint64_t *pau64Contacts,
+ uint32_t u32ScanTime)
+{
+ HRESULT hrc = S_OK;
+
+ PPDMIMOUSEPORT pUpPort = NULL;
+ {
+ AutoReadLock aLock(this COMMA_LOCKVAL_SRC_POS);
+
+ unsigned i;
+ for (i = 0; i < MOUSE_MAX_DEVICES; ++i)
+ {
+ if ( mpDrv[i]
+ && (mpDrv[i]->u32DevCaps & MOUSE_DEVCAP_MULTI_TOUCH))
+ {
+ pUpPort = mpDrv[i]->pUpPort;
+ break;
+ }
+ }
+ }
+
+ if (pUpPort)
+ {
+ int vrc = pUpPort->pfnPutEventMultiTouch(pUpPort, cContacts, pau64Contacts, u32ScanTime);
+ if (RT_FAILURE(vrc))
+ hrc = setError(VBOX_E_IPRT_ERROR,
+ tr("Could not send the multi-touch event to the virtual device (%Rrc)"),
+ vrc);
+ }
+ else
+ {
+ hrc = E_UNEXPECTED;
+ }
+
+ return hrc;
+}
+
/**
* Send an absolute position event to the VMM device.
@@ -356,17 +406,17 @@ HRESULT Mouse::reportAbsEventToMouseDev(int32_t mouseXAbs, int32_t mouseYAbs,
*
* @returns COM status code
*/
-HRESULT Mouse::reportAbsEventToVMMDev(int32_t mouseXAbs, int32_t mouseYAbs)
+HRESULT Mouse::reportAbsEventToVMMDev(int32_t x, int32_t y)
{
- VMMDev *pVMMDev = mParent->getVMMDev();
+ VMMDevMouseInterface *pVMMDev = mParent->getVMMDevMouseInterface();
ComAssertRet(pVMMDev, E_FAIL);
PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
ComAssertRet(pVMMDevPort, E_FAIL);
- if (mouseXAbs != mcLastAbsX || mouseYAbs != mcLastAbsY)
+ if (x != mcLastX || y != mcLastY)
{
int vrc = pVMMDevPort->pfnSetAbsoluteMouse(pVMMDevPort,
- mouseXAbs, mouseYAbs);
+ x, y);
if (RT_FAILURE(vrc))
return setError(VBOX_E_IPRT_ERROR,
tr("Could not send the mouse event to the virtual mouse (%Rrc)"),
@@ -382,7 +432,7 @@ HRESULT Mouse::reportAbsEventToVMMDev(int32_t mouseXAbs, int32_t mouseYAbs)
*
* @returns COM status code
*/
-HRESULT Mouse::reportAbsEvent(int32_t mouseXAbs, int32_t mouseYAbs,
+HRESULT Mouse::reportAbsEvent(int32_t x, int32_t y,
int32_t dz, int32_t dw, uint32_t fButtons,
bool fUsesVMMDevEvent)
{
@@ -397,40 +447,73 @@ HRESULT Mouse::reportAbsEvent(int32_t mouseXAbs, int32_t mouseYAbs,
/*
* Send the absolute mouse position to the VMM device.
*/
- if (mouseXAbs != mcLastAbsX || mouseYAbs != mcLastAbsY)
+ if (x != mcLastX || y != mcLastY)
{
- rc = reportAbsEventToVMMDev(mouseXAbs, mouseYAbs);
+ rc = reportAbsEventToVMMDev(x, y);
cJiggle = !fUsesVMMDevEvent;
}
rc = reportRelEventToMouseDev(cJiggle, 0, dz, dw, fButtons);
}
else
- rc = reportAbsEventToMouseDev(mouseXAbs, mouseYAbs, dz, dw, fButtons);
+ rc = reportAbsEventToMouseDev(x, y, dz, dw, fButtons);
- mcLastAbsX = mouseXAbs;
- mcLastAbsY = mouseYAbs;
+ mcLastX = x;
+ mcLastY = y;
return rc;
}
-#ifndef VBOXBFE_WITHOUT_COM
-void Mouse::fireMouseEvent(bool fAbsolute, LONG x, LONG y, LONG dz, LONG dw, LONG Buttons)
+void Mouse::fireMouseEvent(bool fAbsolute, LONG x, LONG y, LONG dz, LONG dw,
+ LONG fButtons)
{
/* If mouse button is pressed, we generate new event, to avoid reusable events coalescing and thus
dropping key press events */
- if (Buttons != 0)
+ GuestMouseEventMode_T mode;
+ if (fAbsolute)
+ mode = GuestMouseEventMode_Absolute;
+ else
+ mode = GuestMouseEventMode_Relative;
+
+ if (fButtons != 0)
{
VBoxEventDesc evDesc;
- evDesc.init(mEventSource, VBoxEventType_OnGuestMouse, fAbsolute, x, y, dz, dw, Buttons);
+ evDesc.init(mEventSource, VBoxEventType_OnGuestMouse, mode, x, y,
+ dz, dw, fButtons);
evDesc.fire(0);
}
else
{
- mMouseEvent.reinit(VBoxEventType_OnGuestMouse, fAbsolute, x, y, dz, dw, Buttons);
+ mMouseEvent.reinit(VBoxEventType_OnGuestMouse, mode, x, y, dz, dw,
+ fButtons);
mMouseEvent.fire(0);
}
}
-#endif
+void Mouse::fireMultiTouchEvent(uint8_t cContacts,
+ const LONG64 *paContacts,
+ uint32_t u32ScanTime)
+{
+ com::SafeArray<SHORT> xPositions(cContacts);
+ com::SafeArray<SHORT> yPositions(cContacts);
+ com::SafeArray<USHORT> contactIds(cContacts);
+ com::SafeArray<USHORT> contactFlags(cContacts);
+
+ uint8_t i;
+ for (i = 0; i < cContacts; i++)
+ {
+ uint32_t u32Lo = RT_LO_U32(paContacts[i]);
+ uint32_t u32Hi = RT_HI_U32(paContacts[i]);
+ xPositions[i] = (int16_t)u32Lo;
+ yPositions[i] = (int16_t)(u32Lo >> 16);
+ contactIds[i] = RT_BYTE1(u32Hi);
+ contactFlags[i] = RT_BYTE2(u32Hi);
+ }
+
+ VBoxEventDesc evDesc;
+ evDesc.init(mEventSource, VBoxEventType_OnGuestMultiTouch,
+ cContacts, ComSafeArrayAsInParam(xPositions), ComSafeArrayAsInParam(yPositions),
+ ComSafeArrayAsInParam(contactIds), ComSafeArrayAsInParam(contactFlags), u32ScanTime);
+ evDesc.fire(0);
+}
/**
* Send a relative mouse event to the guest.
@@ -442,25 +525,26 @@ void Mouse::fireMouseEvent(bool fAbsolute, LONG x, LONG y, LONG dz, LONG dw, LON
* @param dx X movement
* @param dy Y movement
* @param dz Z movement
- * @param buttonState The mouse button state
+ * @param fButtons The mouse button state
*/
-STDMETHODIMP Mouse::PutMouseEvent(LONG dx, LONG dy, LONG dz, LONG dw, LONG buttonState)
+STDMETHODIMP Mouse::PutMouseEvent(LONG dx, LONG dy, LONG dz, LONG dw,
+ LONG fButtons)
{
HRESULT rc;
- uint32_t fButtons;
+ uint32_t fButtonsAdj;
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
LogRel3(("%s: dx=%d, dy=%d, dz=%d, dw=%d\n", __PRETTY_FUNCTION__,
dx, dy, dz, dw));
- fButtons = mouseButtonsToPDM(buttonState);
+ fButtonsAdj = mouseButtonsToPDM(fButtons);
/* Make sure that the guest knows that we are sending real movement
* events to the PS/2 device and not just dummy wake-up ones. */
updateVMMDevMouseCaps(0, VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE);
- rc = reportRelEventToMouseDev(dx, dy, dz, dw, fButtons);
+ rc = reportRelEventToMouseDev(dx, dy, dz, dw, fButtonsAdj);
- fireMouseEvent(false, dx, dy, dz, dw, buttonState);
+ fireMouseEvent(false, dx, dy, dz, dw, fButtons);
return rc;
}
@@ -480,13 +564,13 @@ STDMETHODIMP Mouse::PutMouseEvent(LONG dx, LONG dy, LONG dz, LONG dw, LONG butto
*
* @returns COM status value
*/
-HRESULT Mouse::convertDisplayRes(LONG x, LONG y, int32_t *pcX, int32_t *pcY,
+HRESULT Mouse::convertDisplayRes(LONG x, LONG y, int32_t *pxAdj, int32_t *pyAdj,
bool *pfValid)
{
- AssertPtrReturn(pcX, E_POINTER);
- AssertPtrReturn(pcY, E_POINTER);
+ AssertPtrReturn(pxAdj, E_POINTER);
+ AssertPtrReturn(pyAdj, E_POINTER);
AssertPtrNullReturn(pfValid, E_POINTER);
- Display *pDisplay = mParent->getDisplay();
+ DisplayMouseInterface *pDisplay = mParent->getDisplayMouseInterface();
ComAssertRet(pDisplay, E_FAIL);
/** The amount to add to the result (multiplied by the screen width/height)
* to compensate for differences in guest methods for mapping back to
@@ -499,27 +583,29 @@ HRESULT Mouse::convertDisplayRes(LONG x, LONG y, int32_t *pcX, int32_t *pcY,
{
ULONG displayWidth, displayHeight;
/* Takes the display lock */
- HRESULT rc = pDisplay->GetScreenResolution(0, &displayWidth,
- &displayHeight, NULL);
+ HRESULT rc = pDisplay->getScreenResolution(0, &displayWidth,
+ &displayHeight, NULL, NULL, NULL);
if (FAILED(rc))
return rc;
- *pcX = displayWidth ? (x * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
- / (LONG) displayWidth: 0;
- *pcY = displayHeight ? (y * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
- / (LONG) displayHeight: 0;
+ *pxAdj = displayWidth ? (x * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
+ / (LONG) displayWidth: 0;
+ *pyAdj = displayHeight ? (y * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
+ / (LONG) displayHeight: 0;
}
else
{
int32_t x1, y1, x2, y2;
/* Takes the display lock */
pDisplay->getFramebufferDimensions(&x1, &y1, &x2, &y2);
- *pcX = x1 < x2 ? ((x - x1) * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
- / (x2 - x1) : 0;
- *pcY = y1 < y2 ? ((y - y1) * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
- / (y2 - y1) : 0;
- if ( *pcX < VMMDEV_MOUSE_RANGE_MIN || *pcX > VMMDEV_MOUSE_RANGE_MAX
- || *pcY < VMMDEV_MOUSE_RANGE_MIN || *pcY > VMMDEV_MOUSE_RANGE_MAX)
+ *pxAdj = x1 < x2 ? ((x - x1) * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
+ / (x2 - x1) : 0;
+ *pyAdj = y1 < y2 ? ((y - y1) * VMMDEV_MOUSE_RANGE + ADJUST_RANGE)
+ / (y2 - y1) : 0;
+ if ( *pxAdj < VMMDEV_MOUSE_RANGE_MIN
+ || *pxAdj > VMMDEV_MOUSE_RANGE_MAX
+ || *pyAdj < VMMDEV_MOUSE_RANGE_MIN
+ || *pyAdj > VMMDEV_MOUSE_RANGE_MAX)
if (pfValid)
*pfValid = false;
}
@@ -540,47 +626,209 @@ HRESULT Mouse::convertDisplayRes(LONG x, LONG y, int32_t *pcX, int32_t *pcY,
* @param x X position (pixel), starting from 1
* @param y Y position (pixel), starting from 1
* @param dz Z movement
- * @param buttonState The mouse button state
+ * @param fButtons The mouse button state
*/
STDMETHODIMP Mouse::PutMouseEventAbsolute(LONG x, LONG y, LONG dz, LONG dw,
- LONG buttonState)
+ LONG fButtons)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- LogRel3(("%s: x=%d, y=%d, dz=%d, dw=%d, buttonState=0x%x\n",
- __PRETTY_FUNCTION__, x, y, dz, dw, buttonState));
+ LogRel3(("%s: x=%d, y=%d, dz=%d, dw=%d, fButtons=0x%x\n",
+ __PRETTY_FUNCTION__, x, y, dz, dw, fButtons));
- int32_t mouseXAbs, mouseYAbs;
- uint32_t fButtons;
+ int32_t xAdj, yAdj;
+ uint32_t fButtonsAdj;
bool fValid;
/** @todo the front end should do this conversion to avoid races */
/** @note Or maybe not... races are pretty inherent in everything done in
* this object and not really bad as far as I can see. */
- HRESULT rc = convertDisplayRes(x, y, &mouseXAbs, &mouseYAbs, &fValid);
+ HRESULT rc = convertDisplayRes(x, y, &xAdj, &yAdj, &fValid);
if (FAILED(rc)) return rc;
- fButtons = mouseButtonsToPDM(buttonState);
+ fButtonsAdj = mouseButtonsToPDM(fButtons);
/* If we are doing old-style (IRQ-less) absolute reporting to the VMM
* device then make sure the guest is aware of it, so that it knows to
* ignore relative movement on the PS/2 device. */
updateVMMDevMouseCaps(VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE, 0);
if (fValid)
{
- rc = reportAbsEvent(mouseXAbs, mouseYAbs, dz, dw, fButtons,
+ rc = reportAbsEvent(xAdj, yAdj, dz, dw, fButtonsAdj,
RT_BOOL( mfVMMDevGuestCaps
& VMMDEV_MOUSE_NEW_PROTOCOL));
- fireMouseEvent(true, x, y, dz, dw, buttonState);
+ fireMouseEvent(true, x, y, dz, dw, fButtons);
}
return rc;
}
+/**
+ * Send a multi-touch event. This requires multi-touch pointing device emulation.
+ * @note all calls out of this object are made with no locks held!
+ *
+ * @returns COM status code.
+ * @param aCount Number of contacts.
+ * @param aContacts Information about each contact.
+ * @param aScanTime Timestamp.
+ */
+STDMETHODIMP Mouse::PutEventMultiTouch(LONG aCount,
+ ComSafeArrayIn(LONG64, aContacts),
+ ULONG aScanTime)
+{
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ com::SafeArray <LONG64> arrayContacts(ComSafeArrayInArg(aContacts));
+
+ LogRel3(("%s: aCount %d(actual %d), aScanTime %u\n",
+ __FUNCTION__, aCount, arrayContacts.size(), aScanTime));
+
+ HRESULT rc = S_OK;
+
+ if ((LONG)arrayContacts.size() >= aCount)
+ {
+ LONG64* paContacts = arrayContacts.raw();
+
+ rc = putEventMultiTouch(aCount, paContacts, aScanTime);
+ }
+ else
+ {
+ rc = E_INVALIDARG;
+ }
+
+ return rc;
+}
+
+/**
+ * Send a multi-touch event. Version for scripting languages.
+ *
+ * @returns COM status code.
+ * @param aCount Number of contacts.
+ * @param aContacts Information about each contact.
+ * @param aScanTime Timestamp.
+ */
+STDMETHODIMP Mouse::PutEventMultiTouchString(LONG aCount,
+ IN_BSTR aContacts,
+ ULONG aScanTime)
+{
+ /** @todo implement: convert the string to LONG64 array and call putEventMultiTouch. */
+ NOREF(aCount);
+ NOREF(aContacts);
+ NOREF(aScanTime);
+ return E_NOTIMPL;
+}
+
+
// private methods
/////////////////////////////////////////////////////////////////////////////
+/* Used by PutEventMultiTouch and PutEventMultiTouchString. */
+HRESULT Mouse::putEventMultiTouch(LONG aCount,
+ LONG64 *paContacts,
+ ULONG aScanTime)
+{
+ if (aCount >= 256)
+ {
+ return E_INVALIDARG;
+ }
+
+ DisplayMouseInterface *pDisplay = mParent->getDisplayMouseInterface();
+ ComAssertRet(pDisplay, E_FAIL);
+
+ /* Touch events are mapped to the primary monitor, because the emulated USB
+ * touchscreen device is associated with one (normally the primary) screen in the guest.
+ */
+ ULONG uScreenId = 0;
+
+ ULONG cWidth = 0;
+ ULONG cHeight = 0;
+ LONG xOrigin = 0;
+ LONG yOrigin = 0;
+ HRESULT rc = pDisplay->getScreenResolution(uScreenId, &cWidth, &cHeight, NULL, &xOrigin, &yOrigin);
+ ComAssertComRCRetRC(rc);
+
+ uint64_t* pau64Contacts = NULL;
+ uint8_t cContacts = 0;
+
+ /* Deliver 0 contacts too, touch device may use this to reset the state. */
+ if (aCount > 0)
+ {
+ /* Create a copy with converted coords. */
+ pau64Contacts = (uint64_t *)RTMemTmpAlloc(aCount * sizeof(uint64_t));
+ if (pau64Contacts)
+ {
+ int32_t x1 = xOrigin;
+ int32_t y1 = yOrigin;
+ int32_t x2 = x1 + cWidth;
+ int32_t y2 = y1 + cHeight;
+
+ LogRel3(("%s: screen [%d] %d,%d %d,%d\n",
+ __FUNCTION__, uScreenId, x1, y1, x2, y2));
+
+ LONG i;
+ for (i = 0; i < aCount; i++)
+ {
+ uint32_t u32Lo = RT_LO_U32(paContacts[i]);
+ uint32_t u32Hi = RT_HI_U32(paContacts[i]);
+ int32_t x = (int16_t)u32Lo;
+ int32_t y = (int16_t)(u32Lo >> 16);
+ uint8_t contactId = RT_BYTE1(u32Hi);
+ bool fInContact = (RT_BYTE2(u32Hi) & 0x1) != 0;
+ bool fInRange = (RT_BYTE2(u32Hi) & 0x2) != 0;
+
+ LogRel3(("%s: [%d] %d,%d id %d, inContact %d, inRange %d\n",
+ __FUNCTION__, i, x, y, contactId, fInContact, fInRange));
+
+ /* x1,y1 are inclusive and x2,y2 are exclusive,
+ * while x,y start from 1 and are inclusive.
+ */
+ if (x <= x1 || x > x2 || y <= y1 || y > y2)
+ {
+ /* Out of range. Skip the contact. */
+ continue;
+ }
+
+ int32_t xAdj = x1 < x2? ((x - 1 - x1) * VMMDEV_MOUSE_RANGE) / (x2 - x1) : 0;
+ int32_t yAdj = y1 < y2? ((y - 1 - y1) * VMMDEV_MOUSE_RANGE) / (y2 - y1) : 0;
+
+ bool fValid = ( xAdj >= VMMDEV_MOUSE_RANGE_MIN
+ && xAdj <= VMMDEV_MOUSE_RANGE_MAX
+ && yAdj >= VMMDEV_MOUSE_RANGE_MIN
+ && yAdj <= VMMDEV_MOUSE_RANGE_MAX);
+
+ if (fValid)
+ {
+ uint8_t fu8 = (fInContact? 0x01: 0x00)
+ | (fInRange? 0x02: 0x00);
+ pau64Contacts[cContacts] = RT_MAKE_U64_FROM_U16((uint16_t)xAdj,
+ (uint16_t)yAdj,
+ RT_MAKE_U16(contactId, fu8),
+ 0);
+ cContacts++;
+ }
+ }
+ }
+ else
+ {
+ rc = E_OUTOFMEMORY;
+ }
+ }
+
+ if (SUCCEEDED(rc))
+ {
+ rc = reportMultiTouchEventToDevice(cContacts, cContacts? pau64Contacts: NULL, (uint32_t)aScanTime);
+
+ /* Send the original contact information. */
+ fireMultiTouchEvent(cContacts, cContacts? paContacts: NULL, (uint32_t)aScanTime);
+ }
+
+ RTMemTmpFree(pau64Contacts);
+
+ return rc;
+}
+
/** Does the guest currently rely on the host to draw the mouse cursor or
* can it switch to doing it itself in software? */
@@ -592,10 +840,11 @@ bool Mouse::guestNeedsHostCursor(void)
/** Check what sort of reporting can be done using the devices currently
* enabled. Does not consider the VMM device. */
-void Mouse::getDeviceCaps(bool *pfAbs, bool *pfRel)
+void Mouse::getDeviceCaps(bool *pfAbs, bool *pfRel, bool *pfMT)
{
bool fAbsDev = false;
bool fRelDev = false;
+ bool fMTDev = false;
AutoReadLock aLock(this COMMA_LOCKVAL_SRC_POS);
@@ -606,11 +855,15 @@ void Mouse::getDeviceCaps(bool *pfAbs, bool *pfRel)
fAbsDev = true;
if (mpDrv[i]->u32DevCaps & MOUSE_DEVCAP_RELATIVE)
fRelDev = true;
+ if (mpDrv[i]->u32DevCaps & MOUSE_DEVCAP_MULTI_TOUCH)
+ fMTDev = true;
}
if (pfAbs)
*pfAbs = fAbsDev;
if (pfRel)
*pfRel = fRelDev;
+ if (pfMT)
+ *pfMT = fMTDev;
}
@@ -619,7 +872,7 @@ bool Mouse::vmmdevCanAbs(void)
{
bool fRelDev;
- getDeviceCaps(NULL, &fRelDev);
+ getDeviceCaps(NULL, &fRelDev, NULL);
return (mfVMMDevGuestCaps & VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE)
&& fRelDev;
}
@@ -630,7 +883,7 @@ bool Mouse::deviceCanAbs(void)
{
bool fAbsDev;
- getDeviceCaps(&fAbsDev, NULL);
+ getDeviceCaps(&fAbsDev, NULL, NULL);
return fAbsDev;
}
@@ -640,7 +893,7 @@ bool Mouse::supportsRel(void)
{
bool fRelDev;
- getDeviceCaps(NULL, &fRelDev);
+ getDeviceCaps(NULL, &fRelDev, NULL);
return fRelDev;
}
@@ -650,22 +903,32 @@ bool Mouse::supportsAbs(void)
{
bool fAbsDev;
- getDeviceCaps(&fAbsDev, NULL);
+ getDeviceCaps(&fAbsDev, NULL, NULL);
return fAbsDev || vmmdevCanAbs();
}
+/** Can we currently send absolute events to the guest? */
+bool Mouse::supportsMT(void)
+{
+ bool fMTDev;
+
+ getDeviceCaps(NULL, NULL, &fMTDev);
+ return fMTDev;
+}
+
+
/** Check what sort of reporting can be done using the devices currently
* enabled (including the VMM device) and notify the guest and the front-end.
*/
void Mouse::sendMouseCapsNotifications(void)
{
- bool fAbsDev, fRelDev, fCanAbs, fNeedsHostCursor;
+ bool fAbsDev, fRelDev, fMTDev, fCanAbs, fNeedsHostCursor;
{
AutoReadLock aLock(this COMMA_LOCKVAL_SRC_POS);
- getDeviceCaps(&fAbsDev, &fRelDev);
+ getDeviceCaps(&fAbsDev, &fRelDev, &fMTDev);
fCanAbs = supportsAbs();
fNeedsHostCursor = guestNeedsHostCursor();
}
@@ -676,7 +939,7 @@ void Mouse::sendMouseCapsNotifications(void)
/** @todo this call takes the Console lock in order to update the cached
* callback data atomically. However I can't see any sign that the cached
* data is ever used again. */
- mParent->onMouseCapabilityChange(fCanAbs, fRelDev, fNeedsHostCursor);
+ mParent->onMouseCapabilityChange(fCanAbs, fRelDev, fMTDev, fNeedsHostCursor);
}
@@ -684,7 +947,7 @@ void Mouse::sendMouseCapsNotifications(void)
* @interface_method_impl{PDMIMOUSECONNECTOR,pfnReportModes}
* A virtual device is notifying us about its current state and capabilities
*/
-DECLCALLBACK(void) Mouse::mouseReportModes(PPDMIMOUSECONNECTOR pInterface, bool fRel, bool fAbs)
+DECLCALLBACK(void) Mouse::mouseReportModes(PPDMIMOUSECONNECTOR pInterface, bool fRel, bool fAbs, bool fMT)
{
PDRVMAINMOUSE pDrv = RT_FROM_MEMBER(pInterface, DRVMAINMOUSE, IConnector);
if (fRel)
@@ -695,6 +958,10 @@ DECLCALLBACK(void) Mouse::mouseReportModes(PPDMIMOUSECONNECTOR pInterface, bool
pDrv->u32DevCaps |= MOUSE_DEVCAP_ABSOLUTE;
else
pDrv->u32DevCaps &= ~MOUSE_DEVCAP_ABSOLUTE;
+ if (fMT)
+ pDrv->u32DevCaps |= MOUSE_DEVCAP_MULTI_TOUCH;
+ else
+ pDrv->u32DevCaps &= ~MOUSE_DEVCAP_MULTI_TOUCH;
pDrv->pMouse->sendMouseCapsNotifications();
}
@@ -722,17 +989,17 @@ DECLCALLBACK(void *) Mouse::drvQueryInterface(PPDMIBASE pInterface, const char
*/
DECLCALLBACK(void) Mouse::drvDestruct(PPDMDRVINS pDrvIns)
{
- PDRVMAINMOUSE pData = PDMINS_2_DATA(pDrvIns, PDRVMAINMOUSE);
- LogFlow(("Mouse::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ PDRVMAINMOUSE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINMOUSE);
+ LogFlow(("Mouse::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
- if (pData->pMouse)
+ if (pThis->pMouse)
{
- AutoWriteLock mouseLock(pData->pMouse COMMA_LOCKVAL_SRC_POS);
+ AutoWriteLock mouseLock(pThis->pMouse COMMA_LOCKVAL_SRC_POS);
for (unsigned cDev = 0; cDev < MOUSE_MAX_DEVICES; ++cDev)
- if (pData->pMouse->mpDrv[cDev] == pData)
+ if (pThis->pMouse->mpDrv[cDev] == pThis)
{
- pData->pMouse->mpDrv[cDev] = NULL;
+ pThis->pMouse->mpDrv[cDev] = NULL;
break;
}
}
@@ -746,9 +1013,9 @@ DECLCALLBACK(void) Mouse::drvDestruct(PPDMDRVINS pDrvIns)
*/
DECLCALLBACK(int) Mouse::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
- PDRVMAINMOUSE pData = PDMINS_2_DATA(pDrvIns, PDRVMAINMOUSE);
- LogFlow(("drvMainMouse_Construct: iInstance=%d\n", pDrvIns->iInstance));
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVMAINMOUSE pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINMOUSE);
+ LogFlow(("drvMainMouse_Construct: iInstance=%d\n", pDrvIns->iInstance));
/*
* Validate configuration.
@@ -764,13 +1031,13 @@ DECLCALLBACK(int) Mouse::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32
*/
pDrvIns->IBase.pfnQueryInterface = Mouse::drvQueryInterface;
- pData->IConnector.pfnReportModes = Mouse::mouseReportModes;
+ pThis->IConnector.pfnReportModes = Mouse::mouseReportModes;
/*
* Get the IMousePort interface of the above driver/device.
*/
- pData->pUpPort = (PPDMIMOUSEPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMIMOUSEPORT_IID);
- if (!pData->pUpPort)
+ pThis->pUpPort = (PPDMIMOUSEPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMIMOUSEPORT_IID);
+ if (!pThis->pUpPort)
{
AssertMsgFailed(("Configuration error: No mouse port interface above!\n"));
return VERR_PDM_MISSING_INTERFACE_ABOVE;
@@ -786,15 +1053,15 @@ DECLCALLBACK(int) Mouse::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32
AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
return rc;
}
- pData->pMouse = (Mouse *)pv; /** @todo Check this cast! */
+ pThis->pMouse = (Mouse *)pv; /** @todo Check this cast! */
unsigned cDev;
{
- AutoReadLock mouseLock(pData->pMouse COMMA_LOCKVAL_SRC_POS);
+ AutoReadLock mouseLock(pThis->pMouse COMMA_LOCKVAL_SRC_POS);
for (cDev = 0; cDev < MOUSE_MAX_DEVICES; ++cDev)
- if (!pData->pMouse->mpDrv[cDev])
+ if (!pThis->pMouse->mpDrv[cDev])
{
- pData->pMouse->mpDrv[cDev] = pData;
+ pThis->pMouse->mpDrv[cDev] = pThis;
break;
}
}
diff --git a/src/VBox/Main/src-client/Nvram.cpp b/src/VBox/Main/src-client/Nvram.cpp
index d1c2a8a4..d3d4200f 100644
--- a/src/VBox/Main/src-client/Nvram.cpp
+++ b/src/VBox/Main/src-client/Nvram.cpp
@@ -4,7 +4,7 @@
*/
/*
- * 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;
@@ -20,6 +20,7 @@
*******************************************************************************/
#include "Nvram.h"
#include "ConsoleImpl.h"
+#include "Global.h"
#include <VBox/vmm/pdm.h>
#include <VBox/vmm/pdmdrv.h>
@@ -44,21 +45,43 @@
typedef struct NVRAM NVRAM;
typedef struct NVRAM *PNVRAM;
+/**
+ * Intstance data associated with PDMDRVINS.
+ */
struct NVRAM
{
- Nvram *pNvram;
- PDMINVRAM INvram;
- int cLoadedVariables;
- bool fPermanentSave;
+ /** Pointer to the associated class instance. */
+ Nvram *pNvram;
+ /** The NVRAM connector interface we provide to DevEFI. */
+ PDMINVRAMCONNECTOR INvramConnector;
+ /** The root of the 'Vars' child of the driver config (i.e.
+ * VBoxInternal/Devices/efi/0/LUN#0/Config/Vars/).
+ * This node has one child node per NVRAM variable. */
+ PCFGMNODE pCfgVarRoot;
+ /** The variable node used in the privous drvNvram_VarQueryByIndex call. */
+ PCFGMNODE pLastVarNode;
+ /** The index pLastVarNode corresponds to. */
+ uint32_t idxLastVar;
+ /** Whether to permanently save the variables or not. */
+ bool fPermanentSave;
};
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** The default NVRAM attribute value (non-volatile, boot servier access,
+ runtime access). */
+#define NVRAM_DEFAULT_ATTRIB UINT32_C(0x7)
+/** The CFGM overlay path of the NVRAM variables. */
+#define NVRAM_CFGM_OVERLAY_PATH "VBoxInternal/Devices/efi/0/LUN#0/Config/Vars"
+
/**
* Constructor/destructor
*/
-Nvram::Nvram(Console *console)
- : mpDrv(NULL),
- mParent(console)
+Nvram::Nvram(Console *pConsole)
+ : mParent(pConsole),
+ mpDrv(NULL)
{
}
@@ -73,149 +96,216 @@ Nvram::~Nvram()
/**
- * @interface_method_impl(PDMINVRAM,pfnStoreNvramValue)
+ * @interface_method_impl(PDMINVRAM,pfnVarStoreSeqEnd)
+ */
+DECLCALLBACK(int) drvNvram_VarStoreSeqEnd(PPDMINVRAMCONNECTOR pInterface, int rc)
+{
+ NOREF(pInterface);
+ return rc;
+}
+
+/**
+ * Converts the binary to a CFGM overlay binary string.
+ *
+ * @returns Pointer to a heap buffer (hand it to RTMemFree when done).
+ * @param pvBuf The binary data to convert.
+ * @param cbBuf The number of bytes to convert.
*/
-DECLCALLBACK(int) drvNvram_pfnStoreNvramValue(PPDMINVRAM pInterface,
- int idxVariable,
- RTUUID *pVendorUuid,
- const char *pcszVariableName,
- size_t cbVariableName,
- uint8_t *pu8Value,
- size_t cbValue)
+static char *drvNvram_binaryToCfgmString(void const *pvBuf, size_t cbBuf)
{
- int rc = VINF_SUCCESS;
- char szExtraDataKey[256];
- char szExtraDataValue[1024];
- LogFlowFunc(("ENTER: pVendorUuid:%RTuuid, pcszVariableName:%s, cbVariableName:%d, pu8Value:%.*Rhxs, cbValue:%d\n",
- pVendorUuid,
- pcszVariableName,
- cbVariableName,
- cbValue,
- pu8Value,
- cbValue));
- PNVRAM pThis = RT_FROM_MEMBER(pInterface, NVRAM, INvram);
- if (!pThis->fPermanentSave)
+ static char s_szPrefix[] = "bytes:";
+ size_t cbStr = RTBase64EncodedLength(cbBuf) + sizeof(s_szPrefix);
+ char *pszStr = (char *)RTMemAlloc(cbStr);
+ if (pszStr)
{
- LogFlowFuncLeaveRC(rc);
- return rc;
+ memcpy(pszStr, s_szPrefix, sizeof(s_szPrefix) - 1);
+ int rc = RTBase64Encode(pvBuf, cbBuf, &pszStr[sizeof(s_szPrefix) - 1], cbStr - sizeof(s_szPrefix) + 1, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pszStr);
+ pszStr = NULL;
+ }
}
+ return pszStr;
+}
- bool fFlushVariable = (!pu8Value);
-
- RT_ZERO(szExtraDataKey);
- RT_ZERO(szExtraDataValue);
- RTStrPrintf(szExtraDataKey, 256, "VBoxInternal/Devices/efi/0/LUN#0/Config/NVRAM/%d/VariableName", idxVariable);
- if (!fFlushVariable)
- RTStrPrintf(szExtraDataValue, 1024, "%s", pcszVariableName);
- pThis->pNvram->getParent()->machine()->SetExtraData(Bstr(szExtraDataKey).raw(), Bstr(szExtraDataValue).raw());
-
- RT_ZERO(szExtraDataKey);
- RT_ZERO(szExtraDataValue);
- RTStrPrintf(szExtraDataKey, 256, "VBoxInternal/Devices/efi/0/LUN#0/Config/NVRAM/%d/VendorGuid", idxVariable);
- if (!fFlushVariable)
- RTUuidToStr(pVendorUuid, szExtraDataValue, 1024);
- pThis->pNvram->getParent()->machine()->SetExtraData(Bstr(szExtraDataKey).raw(), Bstr(szExtraDataValue).raw());
-
- RT_ZERO(szExtraDataKey);
- RT_ZERO(szExtraDataValue);
- RTStrPrintf(szExtraDataKey, 256, "VBoxInternal/Devices/efi/0/LUN#0/Config/NVRAM/%d/VariableValueLength", idxVariable);
- if (!fFlushVariable)
- RTStrPrintf(szExtraDataValue, 1024, "%d", cbValue);
- pThis->pNvram->getParent()->machine()->SetExtraData(Bstr(szExtraDataKey).raw(), Bstr(szExtraDataValue).raw());
-
- RT_ZERO(szExtraDataKey);
- RT_ZERO(szExtraDataValue);
- RTStrPrintf(szExtraDataKey, 256, "VBoxInternal/Devices/efi/0/LUN#0/Config/NVRAM/%d/VariableValue", idxVariable);
- size_t cbActualSize;
- if (pu8Value)
- rc = RTBase64Encode(pu8Value, cbValue, szExtraDataValue, 1024, &cbActualSize);
- AssertRCReturn(rc, rc);
- pThis->pNvram->getParent()->machine()->SetExtraData(Bstr(szExtraDataKey).raw(), Bstr(szExtraDataValue).raw());
+/**
+ * @interface_method_impl(PDMINVRAM,pfnVarStoreSeqPut)
+ */
+DECLCALLBACK(int) drvNvram_VarStoreSeqPut(PPDMINVRAMCONNECTOR pInterface, int idxVariable,
+ PCRTUUID pVendorUuid, const char *pszName, size_t cchName,
+ uint32_t fAttributes, uint8_t const *pbValue, size_t cbValue)
+{
+ PNVRAM pThis = RT_FROM_MEMBER(pInterface, NVRAM, INvramConnector);
+ int rc = VINF_SUCCESS;
+ if (pThis->fPermanentSave && pThis->pNvram)
+ {
+ char szExtraName[256];
+ size_t offValueNm = RTStrPrintf(szExtraName, sizeof(szExtraName) - 16,
+ NVRAM_CFGM_OVERLAY_PATH "/%04u/", idxVariable);
+
+ char szUuid[RTUUID_STR_LENGTH];
+ int rc2 = RTUuidToStr(pVendorUuid, szUuid, sizeof(szUuid)); AssertRC(rc2);
+
+ char szAttribs[32];
+ if (fAttributes != NVRAM_DEFAULT_ATTRIB)
+ RTStrPrintf(szAttribs, sizeof(szAttribs), "%#x", fAttributes);
+ else
+ szAttribs[0] = '\0';
+
+ char *pszValue = drvNvram_binaryToCfgmString(pbValue, cbValue);
+ if (pszValue)
+ {
+ const char *apszTodo[] =
+ {
+ "Name", pszName,
+ "Uuid", szUuid,
+ "Value", pszValue,
+ "Attribs", szAttribs,
+ };
+ for (unsigned i = 0; i < RT_ELEMENTS(apszTodo); i += 2)
+ {
+ if (!apszTodo[i + 1][0])
+ continue;
+
+ Assert(strlen(apszTodo[i]) < 16);
+ strcpy(szExtraName + offValueNm, apszTodo[i]);
+ try
+ {
+ HRESULT hrc = pThis->pNvram->getParent()->machine()->SetExtraData(Bstr(szExtraName).raw(),
+ Bstr(apszTodo[i + 1]).raw());
+ if (FAILED(hrc))
+ {
+ LogRel(("drvNvram_deleteVar: SetExtraData(%s,%s) returned %Rhrc\n", szExtraName, apszTodo[i + 1], hrc));
+ rc = Global::vboxStatusCodeFromCOM(hrc);
+ }
+ }
+ catch (...)
+ {
+ LogRel(("drvNvram_deleteVar: SetExtraData(%s,%s) threw exception\n", szExtraName, apszTodo[i + 1]));
+ rc = VERR_UNEXPECTED_EXCEPTION;
+ }
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTMemFree(pszValue);
+ }
+
+ NOREF(cchName);
LogFlowFuncLeaveRC(rc);
return rc;
}
-
/**
- * @interface_method_impl(PDMINVRAM,pfnFlushNvramStorage)
+ * Deletes a variable.
+ *
+ * @param pThis The NVRAM driver instance data.
+ * @param pszVarNodeNm The variable node name.
*/
-DECLCALLBACK(int) drvNvram_pfnFlushNvramStorage(PPDMINVRAM pInterface)
+static void drvNvram_deleteVar(PNVRAM pThis, const char *pszVarNodeNm)
{
- int rc = VINF_SUCCESS;
- LogFlowFuncEnter();
- PNVRAM pThis = RT_FROM_MEMBER(pInterface, NVRAM, INvram);
- if (!pThis->fPermanentSave)
+ char szExtraName[256];
+ size_t offValue = RTStrPrintf(szExtraName, sizeof(szExtraName) - 16, NVRAM_CFGM_OVERLAY_PATH "/%s/", pszVarNodeNm);
+ static const char *s_apszValueNames[] = { "Name", "Uuid", "Value", "Attribs" };
+ for (unsigned i = 0; i < RT_ELEMENTS(s_apszValueNames); i++)
{
- LogFlowFuncLeaveRC(rc);
- return rc;
+ Assert(strlen(s_apszValueNames[i]) < 16);
+ strcpy(szExtraName + offValue, s_apszValueNames[i]);
+ try
+ {
+ HRESULT hrc = pThis->pNvram->getParent()->machine()->SetExtraData(Bstr(szExtraName).raw(), Bstr().raw());
+ if (FAILED(hrc))
+ LogRel(("drvNvram_deleteVar: SetExtraData(%s,) returned %Rhrc\n", szExtraName, hrc));
+ }
+ catch (...)
+ {
+ LogRel(("drvNvram_deleteVar: SetExtraData(%s,) threw exception\n", szExtraName));
+ }
}
+}
- for (int idxVariable = 0; idxVariable < pThis->cLoadedVariables; ++idxVariable)
+/**
+ * @interface_method_impl(PDMINVRAM,pfnVarStoreSeqBegin)
+ */
+DECLCALLBACK(int) drvNvram_VarStoreSeqBegin(PPDMINVRAMCONNECTOR pInterface, uint32_t cVariables)
+{
+ PNVRAM pThis = RT_FROM_MEMBER(pInterface, NVRAM, INvramConnector);
+ int rc = VINF_SUCCESS;
+ if (pThis->fPermanentSave && pThis->pNvram)
{
- drvNvram_pfnStoreNvramValue(pInterface, idxVariable, NULL, NULL, 0, NULL, 0);
+ /*
+ * Remove all existing variables.
+ */
+ for (PCFGMNODE pVarNode = CFGMR3GetFirstChild(pThis->pCfgVarRoot); pVarNode; pVarNode = CFGMR3GetNextChild(pVarNode))
+ {
+ char szName[128];
+ rc = CFGMR3GetName(pVarNode, szName, sizeof(szName));
+ if (RT_SUCCESS(rc))
+ drvNvram_deleteVar(pThis, szName);
+ else
+ LogRel(("drvNvram_VarStoreSeqBegin: CFGMR3GetName -> %Rrc\n", rc));
+ }
}
- LogFlowFuncLeaveRC(rc);
+
+ NOREF(cVariables);
return rc;
}
-
/**
- * @interface_method_impl(PDMINVRAM,pfnStoreNvramValue)
+ * @interface_method_impl(PDMINVRAMCONNECTOR,pfnVarQueryByIndex)
*/
-DECLCALLBACK(int) drvNvram_pfnLoadNvramValue(PPDMINVRAM pInterface,
- int idxVariable,
- RTUUID *pVendorUuid,
- char *pcszVariableName,
- size_t *pcbVariableName,
- uint8_t *pu8Value,
- size_t *pcbValue)
+DECLCALLBACK(int) drvNvram_VarQueryByIndex(PPDMINVRAMCONNECTOR pInterface, uint32_t idxVariable,
+ PRTUUID pVendorUuid, char *pszName, uint32_t *pcchName,
+ uint32_t *pfAttributes, uint8_t *pbValue, uint32_t *pcbValue)
{
- int rc = VINF_SUCCESS;
- char szExtraDataKey[256];
- Bstr bstrValue;
- HRESULT hrc;
- LogFlowFunc(("ENTER: idxVariable:%d, *pcbVariableName:%d, *pcbValue:%d\n",
- idxVariable,
- *pcbVariableName,
- *pcbValue));
- PNVRAM pThis = RT_FROM_MEMBER(pInterface, NVRAM, INvram);
- if (!pThis->fPermanentSave)
+ PNVRAM pThis = RT_FROM_MEMBER(pInterface, NVRAM, INvramConnector);
+
+ /*
+ * Find the requested variable node.
+ */
+ PCFGMNODE pVarNode;
+ if (pThis->idxLastVar + 1 == idxVariable && pThis->pLastVarNode)
+ pVarNode = CFGMR3GetNextChild(pThis->pLastVarNode);
+ else
{
- rc = VERR_NOT_FOUND;
- LogFlowFuncLeaveRC(rc);
- return rc;
+ pVarNode = CFGMR3GetFirstChild(pThis->pCfgVarRoot);
+ for (uint32_t i = 0; i < idxVariable && pVarNode; i++)
+ pVarNode = CFGMR3GetNextChild(pVarNode);
}
+ if (!pVarNode)
+ return VERR_NOT_FOUND;
+ /* cache it */
+ pThis->pLastVarNode = pVarNode;
+ pThis->idxLastVar = idxVariable;
- RT_ZERO(szExtraDataKey);
- RTStrPrintf(szExtraDataKey, 256, "VBoxInternal/Devices/efi/0/LUN#0/Config/NVRAM/%d/VariableName", idxVariable);
- hrc = pThis->pNvram->getParent()->machine()->GetExtraData(Bstr(szExtraDataKey).raw(), bstrValue.asOutParam());
- if (!SUCCEEDED(hrc))
- return VERR_NOT_FOUND;
- *pcbVariableName = RTStrCopy(pcszVariableName, 1024, Utf8Str(bstrValue).c_str());
-
- RT_ZERO(szExtraDataKey);
- RTStrPrintf(szExtraDataKey, 256, "VBoxInternal/Devices/efi/0/LUN#0/Config/NVRAM/%d/VendorGuid", idxVariable);
- hrc = pThis->pNvram->getParent()->machine()->GetExtraData(Bstr(szExtraDataKey).raw(), bstrValue.asOutParam());
- RTUuidFromStr(pVendorUuid, Utf8Str(bstrValue).c_str());
-
-#if 0
- RT_ZERO(szExtraDataKey);
- RTStrPrintf(szExtraDataKey, 256, "VBoxInternal/Devices/efi/0/LUN#0/Config/NVRAM/%d/VariableValueLength", idxVariable);
- hrc = pThis->pNvram->getParent()->machine()->GetExtraData(Bstr(szExtraDataKey).raw(), bstrValue.asOutParam());
- *pcbValue = Utf8Str(bstrValue).toUInt32();
-#endif
-
- RT_ZERO(szExtraDataKey);
- RTStrPrintf(szExtraDataKey, 256, "VBoxInternal/Devices/efi/0/LUN#0/Config/NVRAM/%d/VariableValue", idxVariable);
- hrc = pThis->pNvram->getParent()->machine()->GetExtraData(Bstr(szExtraDataKey).raw(), bstrValue.asOutParam());
- rc = RTBase64Decode(Utf8Str(bstrValue).c_str(), pu8Value, 1024, pcbValue, NULL);
+ /*
+ * Read the variable node.
+ */
+ int rc = CFGMR3QueryString(pVarNode, "Name", pszName, *pcchName);
AssertRCReturn(rc, rc);
+ *pcchName = (uint32_t)strlen(pszName);
- pThis->cLoadedVariables++;
- LogFlowFuncLeaveRC(rc);
- return rc;
+ char szUuid[RTUUID_STR_LENGTH];
+ rc = CFGMR3QueryString(pVarNode, "Uuid", szUuid, sizeof(szUuid));
+ AssertRCReturn(rc, rc);
+ rc = RTUuidFromStr(pVendorUuid, szUuid);
+ AssertRCReturn(rc, rc);
+
+ rc = CFGMR3QueryU32Def(pVarNode, "Attribs", pfAttributes, NVRAM_DEFAULT_ATTRIB);
+ AssertRCReturn(rc, rc);
+
+ size_t cbValue;
+ rc = CFGMR3QuerySize(pVarNode, "Value", &cbValue);
+ AssertRCReturn(rc, rc);
+ AssertReturn(cbValue <= *pcbValue, VERR_BUFFER_OVERFLOW);
+ rc = CFGMR3QueryBytes(pVarNode, "Value", pbValue, cbValue);
+ AssertRCReturn(rc, rc);
+ *pcbValue = (uint32_t)cbValue;
+
+ return VINF_SUCCESS;
}
@@ -229,7 +319,7 @@ DECLCALLBACK(void *) Nvram::drvNvram_QueryInterface(PPDMIBASE pInterface, const
PNVRAM pThis = PDMINS_2_DATA(pDrvIns, PNVRAM);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
- PDMIBASE_RETURN_INTERFACE(pszIID, PDMINVRAM, &pThis->INvram);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINVRAMCONNECTOR, &pThis->INvramConnector);
return NULL;
}
@@ -239,8 +329,11 @@ DECLCALLBACK(void *) Nvram::drvNvram_QueryInterface(PPDMIBASE pInterface, const
*/
DECLCALLBACK(void) Nvram::drvNvram_Destruct(PPDMDRVINS pDrvIns)
{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
LogFlow(("%s: iInstance/#d\n", __FUNCTION__, pDrvIns->iInstance));
PNVRAM pThis = PDMINS_2_DATA(pDrvIns, PNVRAM);
+ if (pThis->pNvram != NULL)
+ pThis->pNvram->mpDrv = NULL;
}
@@ -249,9 +342,29 @@ DECLCALLBACK(void) Nvram::drvNvram_Destruct(PPDMDRVINS pDrvIns)
*/
DECLCALLBACK(int) Nvram::drvNvram_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
LogFlowFunc(("iInstance/#d, pCfg:%p, fFlags:%x\n", pDrvIns->iInstance, pCfg, fFlags));
PNVRAM pThis = PDMINS_2_DATA(pDrvIns, PNVRAM);
+ /*
+ * Initalize instance data variables first.
+ */
+ //pThis->pNvram = NULL;
+ //pThis->cLoadedVariables = 0;
+ //pThis->fPermanentSave = false;
+ pThis->pCfgVarRoot = CFGMR3GetChild(pCfg, "Vars");
+ //pThis->pLastVarNode = NULL;
+ pThis->idxLastVar = UINT32_MAX / 2;
+
+ pDrvIns->IBase.pfnQueryInterface = Nvram::drvNvram_QueryInterface;
+ pThis->INvramConnector.pfnVarQueryByIndex = drvNvram_VarQueryByIndex;
+ pThis->INvramConnector.pfnVarStoreSeqBegin = drvNvram_VarStoreSeqBegin;
+ pThis->INvramConnector.pfnVarStoreSeqPut = drvNvram_VarStoreSeqPut;
+ pThis->INvramConnector.pfnVarStoreSeqEnd = drvNvram_VarStoreSeqEnd;
+
+ /*
+ * Validate and read configuration.
+ */
if (!CFGMR3AreValuesValid(pCfg, "Object\0"
"PermanentSave\0"))
return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
@@ -259,23 +372,16 @@ DECLCALLBACK(int) Nvram::drvNvram_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg,
("Configuration error: Not possible to attach anything to this driver!\n"),
VERR_PDM_DRVINS_NO_ATTACH);
- void *pv;
- int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
+ int rc = CFGMR3QueryPtr(pCfg, "Object", (void **)&pThis->pNvram);
AssertMsgRCReturn(rc, ("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc), rc);
- pThis->pNvram = (Nvram *)pv;
- bool fPermanentSave = false;
- rc = CFGMR3QueryBool(pCfg, "PermanentSave", &fPermanentSave);
- if ( RT_SUCCESS(rc)
- || rc == VERR_CFGM_VALUE_NOT_FOUND)
- pThis->fPermanentSave = fPermanentSave;
- else
- AssertRCReturn(rc, rc);
+ rc = CFGMR3QueryBoolDef(pCfg, "PermanentSave", &pThis->fPermanentSave, false);
+ AssertRCReturn(rc, rc);
- pDrvIns->IBase.pfnQueryInterface = Nvram::drvNvram_QueryInterface;
- pThis->INvram.pfnFlushNvramStorage = drvNvram_pfnFlushNvramStorage;
- pThis->INvram.pfnStoreNvramValue = drvNvram_pfnStoreNvramValue;
- pThis->INvram.pfnLoadNvramValue = drvNvram_pfnLoadNvramValue;
+ /*
+ * Let the associated class instance know about us.
+ */
+ pThis->pNvram->mpDrv = pThis;
return VINF_SUCCESS;
}
diff --git a/src/VBox/Main/src-client/PCIRawDevImpl.cpp b/src/VBox/Main/src-client/PCIRawDevImpl.cpp
index 35617c8a..b60adf43 100644
--- a/src/VBox/Main/src-client/PCIRawDevImpl.cpp
+++ b/src/VBox/Main/src-client/PCIRawDevImpl.cpp
@@ -14,6 +14,7 @@
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
+
#include "Logging.h"
#include "PCIRawDevImpl.h"
#include "PCIDeviceAttachmentImpl.h"
@@ -104,41 +105,33 @@ DECLCALLBACK(int) PCIRawDev::drvDeviceConstructComplete(PPDMIPCIRAWCONNECTOR pIn
/**
- * Destruct a PCI raw driver instance.
- *
- * @returns VBox status.
- * @param pDrvIns The driver instance data.
+ * @interface_method_impl{PDMDRVREG,pfnReset}
*/
-DECLCALLBACK(void) PCIRawDev::drvDestruct(PPDMDRVINS pDrvIns)
+DECLCALLBACK(void) PCIRawDev::drvReset(PPDMDRVINS pDrvIns)
{
- PDRVMAINPCIRAWDEV pData = PDMINS_2_DATA(pDrvIns, PDRVMAINPCIRAWDEV);
- PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
-
- if (pData->pPCIRawDev)
- pData->pPCIRawDev->mpDrv = NULL;
}
/**
- * Reset notification.
- *
- * @returns VBox status.
- * @param pDrvIns The driver instance data.
+ * @interface_method_impl{PDMDRVREG,pfnReset}
*/
-DECLCALLBACK(void) PCIRawDev::drvReset(PPDMDRVINS pDrvIns)
+DECLCALLBACK(void) PCIRawDev::drvDestruct(PPDMDRVINS pDrvIns)
{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ PDRVMAINPCIRAWDEV pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINPCIRAWDEV);
+
+ if (pThis->pPCIRawDev)
+ pThis->pPCIRawDev->mpDrv = NULL;
}
/**
- * Construct a raw PCI driver instance.
- *
- * @copydoc FNPDMDRVCONSTRUCT
+ * @interface_method_impl{PDMDRVREG,pfnConstruct}
*/
DECLCALLBACK(int) PCIRawDev::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
{
- PDRVMAINPCIRAWDEV pData = PDMINS_2_DATA(pDrvIns, PDRVMAINPCIRAWDEV);
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVMAINPCIRAWDEV pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINPCIRAWDEV);
/*
* Validate configuration.
@@ -158,7 +151,7 @@ DECLCALLBACK(int) PCIRawDev::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHand
/*
* IConnector.
*/
- pData->IConnector.pfnDeviceConstructComplete = PCIRawDev::drvDeviceConstructComplete;
+ pThis->IConnector.pfnDeviceConstructComplete = PCIRawDev::drvDeviceConstructComplete;
/*
* Get the object pointer and update the mpDrv member.
@@ -171,8 +164,8 @@ DECLCALLBACK(int) PCIRawDev::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHand
return rc;
}
- pData->pPCIRawDev = (PCIRawDev *)pv;
- pData->pPCIRawDev->mpDrv = pData;
+ pThis->pPCIRawDev = (PCIRawDev *)pv;
+ pThis->pPCIRawDev->mpDrv = pThis;
return VINF_SUCCESS;
}
@@ -227,3 +220,4 @@ const PDMDRVREG PCIRawDev::DrvReg =
/* u32EndVersion */
PDM_DRVREG_VERSION
};
+
diff --git a/src/VBox/Main/src-client/RemoteUSBBackend.cpp b/src/VBox/Main/src-client/RemoteUSBBackend.cpp
index c8d7811f..83d30b15 100644
--- a/src/VBox/Main/src-client/RemoteUSBBackend.cpp
+++ b/src/VBox/Main/src-client/RemoteUSBBackend.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -900,7 +900,7 @@ bool RemoteUSBBackend::addUUID (const Guid *pUuid)
unsigned i;
for (i = 0; i < RT_ELEMENTS(aGuids); i++)
{
- if (aGuids[i].isEmpty ())
+ if (aGuids[i].isZero())
{
aGuids[i] = *pUuid;
return true;
@@ -961,7 +961,7 @@ RemoteUSBBackend::RemoteUSBBackend(Console *console, ConsoleVRDPServer *server,
if (RT_FAILURE(rc))
{
AssertFailed ();
- memset (&mCritsect, 0, sizeof (mCritsect));
+ RT_ZERO(mCritsect);
}
mCallback.pInstance = (PREMOTEUSBBACKEND)this;
@@ -1320,7 +1320,7 @@ int RemoteUSBBackend::reapURB (const void *pvBody, uint32_t cbBody)
/* And insert it to its new place. */
if (pDevice->pHeadQURBs->fCompleted)
{
- /* At least one other completed URB; insert after the
+ /* At least one other completed URB; insert after the
* last completed URB.
*/
REMOTEUSBQURB *prev_qurb = pDevice->pHeadQURBs;
@@ -1341,7 +1341,7 @@ int RemoteUSBBackend::reapURB (const void *pvBody, uint32_t cbBody)
/* No other completed URBs; insert at head. */
qurb->next = pDevice->pHeadQURBs;
qurb->prev = NULL;
-
+
pDevice->pHeadQURBs->prev = qurb;
pDevice->pHeadQURBs = qurb;
}
diff --git a/src/VBox/Main/src-client/RemoteUSBDeviceImpl.cpp b/src/VBox/Main/src-client/RemoteUSBDeviceImpl.cpp
index 92a2338c..99986690 100644
--- a/src/VBox/Main/src-client/RemoteUSBDeviceImpl.cpp
+++ b/src/VBox/Main/src-client/RemoteUSBDeviceImpl.cpp
@@ -7,7 +7,7 @@
*/
/*
- * Copyright (C) 2006-2008 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Main/src-client/SessionImpl.cpp b/src/VBox/Main/src-client/SessionImpl.cpp
index 5c030550..6d2cbbd0 100644
--- a/src/VBox/Main/src-client/SessionImpl.cpp
+++ b/src/VBox/Main/src-client/SessionImpl.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;
@@ -15,17 +15,10 @@
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
-#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
-# include <errno.h>
-# include <sys/types.h>
-# include <sys/stat.h>
-# include <sys/ipc.h>
-# include <sys/sem.h>
-#endif
-
#include "SessionImpl.h"
#include "ConsoleImpl.h"
#include "Global.h"
+#include "ClientTokenHolder.h"
#include "AutoCaller.h"
#include "Logging.h"
@@ -33,11 +26,6 @@
#include <VBox/err.h>
#include <iprt/process.h>
-#if defined(RT_OS_WINDOWS) || defined (RT_OS_OS2)
-/** VM IPC mutex holder thread */
-static DECLCALLBACK(int) IPCMutexHolderThread(RTTHREAD Thread, void *pvUser);
-#endif
-
/**
* Local macro to check whether the session is open and return an error if not.
* @note Don't forget to do |Auto[Reader]Lock alock (this);| before using this
@@ -89,17 +77,7 @@ HRESULT Session::init()
mState = SessionState_Unlocked;
mType = SessionType_Null;
-#if defined(RT_OS_WINDOWS)
- mIPCSem = NULL;
- mIPCThreadSem = NULL;
-#elif defined(RT_OS_OS2)
- mIPCThread = NIL_RTTHREAD;
- mIPCThreadSem = NIL_RTSEMEVENT;
-#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
- mIPCSem = -1;
-#else
-# error "Port me!"
-#endif
+ mClientTokenHolder = NULL;
/* Confirm a successful initialization when it's the case */
autoInitSpan.setSucceeded();
@@ -186,16 +164,21 @@ STDMETHODIMP Session::COMGETTER(Machine)(IMachine **aMachine)
CHECK_OPEN();
HRESULT rc;
+#ifndef VBOX_COM_INPROC_API_CLIENT
if (mConsole)
rc = mConsole->machine().queryInterfaceTo(aMachine);
else
+#endif
rc = mRemoteMachine.queryInterfaceTo(aMachine);
if (FAILED(rc))
{
/** @todo VBox 3.3: replace E_FAIL with rc here. */
+#ifndef VBOX_COM_INPROC_API_CLIENT
if (mConsole)
setError(E_FAIL, tr("Failed to query the session machine (%Rhrc)"), rc);
- else if (FAILED_DEAD_INTERFACE(rc))
+ else
+#endif
+ if (FAILED_DEAD_INTERFACE(rc))
setError(E_FAIL, tr("Peer process crashed"));
else
setError(E_FAIL, tr("Failed to query the remote session machine (%Rhrc)"), rc);
@@ -216,17 +199,22 @@ STDMETHODIMP Session::COMGETTER(Console)(IConsole **aConsole)
CHECK_OPEN();
HRESULT rc;
+#ifndef VBOX_COM_INPROC_API_CLIENT
if (mConsole)
rc = mConsole.queryInterfaceTo(aConsole);
else
+#endif
rc = mRemoteConsole.queryInterfaceTo(aConsole);
if (FAILED(rc))
{
/** @todo VBox 3.3: replace E_FAIL with rc here. */
+#ifndef VBOX_COM_INPROC_API_CLIENT
if (mConsole)
setError(E_FAIL, tr("Failed to query the console (%Rhrc)"), rc);
- else if (FAILED_DEAD_INTERFACE(rc))
+ else
+#endif
+ if (FAILED_DEAD_INTERFACE(rc))
setError(E_FAIL, tr("Peer process crashed"));
else
setError(E_FAIL, tr("Failed to query the remote console (%Rhrc)"), rc);
@@ -283,6 +271,7 @@ STDMETHODIMP Session::GetRemoteConsole(IConsole **aConsole)
AssertReturn(mState != SessionState_Unlocked, VBOX_E_INVALID_VM_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertMsgReturn(mType == SessionType_WriteLock && !!mConsole,
("This is not a direct session!\n"),
VBOX_E_INVALID_OBJECT_STATE);
@@ -297,9 +286,20 @@ STDMETHODIMP Session::GetRemoteConsole(IConsole **aConsole)
LogFlowThisFuncLeave();
return S_OK;
+
+#else /* VBOX_COM_INPROC_API_CLIENT */
+ AssertFailed();
+ return VBOX_E_INVALID_OBJECT_STATE;
+#endif /* VBOX_COM_INPROC_API_CLIENT */
}
-STDMETHODIMP Session::AssignMachine(IMachine *aMachine, LockType_T aLockType)
+#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
+STDMETHODIMP Session::AssignMachine(IMachine *aMachine, LockType_T aLockType,
+ IN_BSTR aTokenId)
+#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
+STDMETHODIMP Session::AssignMachine(IMachine *aMachine, LockType_T aLockType,
+ IToken *aToken)
+#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
{
LogFlowThisFuncEnter();
LogFlowThisFunc(("aMachine=%p\n", aMachine));
@@ -327,19 +327,46 @@ STDMETHODIMP Session::AssignMachine(IMachine *aMachine, LockType_T aLockType)
return S_OK;
}
- HRESULT rc = E_FAIL;
-
/* query IInternalMachineControl interface */
mControl = aMachine;
AssertReturn(!!mControl, E_FAIL);
- rc = mConsole.createObject();
+#ifndef VBOX_COM_INPROC_API_CLIENT
+ HRESULT rc = mConsole.createObject();
AssertComRCReturn(rc, rc);
rc = mConsole->init(aMachine, mControl, aLockType);
AssertComRCReturn(rc, rc);
+#else
+ HRESULT rc = S_OK;
+ mRemoteMachine = aMachine;
+#endif
- rc = grabIPCSemaphore();
+#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
+ Utf8Str strTokenId(aTokenId);
+ Assert(!strTokenId.isEmpty());
+#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
+ AssertPtr(aToken);
+#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
+ /* create the machine client token */
+ try
+ {
+#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
+ mClientTokenHolder = new ClientTokenHolder(strTokenId);
+#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
+ mClientTokenHolder = new ClientTokenHolder(aToken);
+#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
+ if (!mClientTokenHolder->isReady())
+ {
+ delete mClientTokenHolder;
+ mClientTokenHolder = NULL;
+ rc = E_FAIL;
+ }
+ }
+ catch (std::bad_alloc &)
+ {
+ rc = E_OUTOFMEMORY;
+ }
/*
* Reference the VirtualBox object to ensure the server is up
@@ -357,11 +384,13 @@ STDMETHODIMP Session::AssignMachine(IMachine *aMachine, LockType_T aLockType)
{
/* some cleanup */
mControl.setNull();
+#ifndef VBOX_COM_INPROC_API_CLIENT
if (!mConsole.isNull())
{
mConsole->uninit();
mConsole.setNull();
}
+#endif
}
LogFlowThisFunc(("rc=%08X\n", rc));
@@ -469,9 +498,13 @@ STDMETHODIMP Session::UpdateMachineState(MachineState_T aMachineState)
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
AssertReturn(!mControl.isNull(), E_FAIL);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(!mConsole.isNull(), E_FAIL);
return mConsole->updateMachineState(aMachineState);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::Uninitialize()
@@ -495,10 +528,11 @@ STDMETHODIMP Session::Uninitialize()
return S_OK;
}
-#ifndef DEBUG_andy /* Don't bug me -- now time to fix this at the moment. */
- AssertReturn(mState == SessionState_Locked ||
- mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE);
-#endif
+ AssertMsgReturn( mState == SessionState_Locked
+ || mState == SessionState_Spawning,
+ ("Session is in wrong state (%ld), expected locked (%ld) or spawning (%ld)\n",
+ mState, SessionState_Locked, SessionState_Spawning),
+ VBOX_E_INVALID_VM_STATE);
/* close ourselves */
rc = unlockMachine(false /* aFinalRelease */, true /* aFromServer */);
@@ -533,9 +567,13 @@ STDMETHODIMP Session::OnNetworkAdapterChange(INetworkAdapter *networkAdapter, BO
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onNetworkAdapterChange(networkAdapter, changeAdapter);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnSerialPortChange(ISerialPort *serialPort)
@@ -548,9 +586,13 @@ STDMETHODIMP Session::OnSerialPortChange(ISerialPort *serialPort)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onSerialPortChange(serialPort);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnParallelPortChange(IParallelPort *parallelPort)
@@ -563,9 +605,13 @@ STDMETHODIMP Session::OnParallelPortChange(IParallelPort *parallelPort)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onParallelPortChange(parallelPort);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnStorageControllerChange()
@@ -578,9 +624,13 @@ STDMETHODIMP Session::OnStorageControllerChange()
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onStorageControllerChange();
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
@@ -593,9 +643,13 @@ STDMETHODIMP Session::OnMediumChange(IMediumAttachment *aMediumAttachment, BOOL
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onMediumChange(aMediumAttachment, aForce);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnCPUChange(ULONG aCPU, BOOL aRemove)
@@ -608,9 +662,13 @@ STDMETHODIMP Session::OnCPUChange(ULONG aCPU, BOOL aRemove)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onCPUChange(aCPU, aRemove);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnCPUExecutionCapChange(ULONG aExecutionCap)
@@ -623,9 +681,13 @@ STDMETHODIMP Session::OnCPUExecutionCapChange(ULONG aExecutionCap)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onCPUExecutionCapChange(aExecutionCap);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnVRDEServerChange(BOOL aRestart)
@@ -638,9 +700,32 @@ STDMETHODIMP Session::OnVRDEServerChange(BOOL aRestart)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onVRDEServerChange(aRestart);
+#else
+ return S_OK;
+#endif
+}
+
+STDMETHODIMP Session::OnVideoCaptureChange()
+{
+ LogFlowThisFunc(("\n"));
+
+ AutoCaller autoCaller(this);
+ AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
+ AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
+ AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
+
+ return mConsole->onVideoCaptureChange();
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnUSBControllerChange()
@@ -653,9 +738,13 @@ STDMETHODIMP Session::OnUSBControllerChange()
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onUSBControllerChange();
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnSharedFolderChange(BOOL aGlobal)
@@ -668,9 +757,13 @@ STDMETHODIMP Session::OnSharedFolderChange(BOOL aGlobal)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onSharedFolderChange(aGlobal);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnClipboardModeChange(ClipboardMode_T aClipboardMode)
@@ -683,9 +776,13 @@ STDMETHODIMP Session::OnClipboardModeChange(ClipboardMode_T aClipboardMode)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onClipboardModeChange(aClipboardMode);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
@@ -697,9 +794,13 @@ STDMETHODIMP Session::OnDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onDragAndDropModeChange(aDragAndDropMode);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnUSBDeviceAttach(IUSBDevice *aDevice,
@@ -714,9 +815,13 @@ STDMETHODIMP Session::OnUSBDeviceAttach(IUSBDevice *aDevice,
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onUSBDeviceAttach(aDevice, aError, aMaskedIfs);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnUSBDeviceDetach(IN_BSTR aId,
@@ -730,9 +835,13 @@ STDMETHODIMP Session::OnUSBDeviceDetach(IN_BSTR aId,
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onUSBDeviceDetach(aId, aError);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
@@ -743,7 +852,9 @@ STDMETHODIMP Session::OnShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
+#endif
if (mState != SessionState_Locked)
{
@@ -756,7 +867,11 @@ STDMETHODIMP Session::OnShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
return aCheck ? S_OK : E_FAIL;
}
+#ifndef VBOX_COM_INPROC_API_CLIENT
return mConsole->onShowWindow(aCheck, aCanShow, aWinId);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::OnBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
@@ -769,12 +884,16 @@ STDMETHODIMP Session::OnBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
return mConsole->onBandwidthGroupChange(aBandwidthGroup);
+#else
+ return S_OK;
+#endif
}
-STDMETHODIMP Session::OnStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove)
+STDMETHODIMP Session::OnStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove, BOOL aSilent)
{
LogFlowThisFunc(("\n"));
@@ -784,15 +903,20 @@ STDMETHODIMP Session::OnStorageDeviceChange(IMediumAttachment *aMediumAttachment
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
- return mConsole->onStorageDeviceChange(aMediumAttachment, aRemove);
+ return mConsole->onStorageDeviceChange(aMediumAttachment, aRemove, aSilent);
+#else
+ return S_OK;
+#endif
}
STDMETHODIMP Session::AccessGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags,
BOOL aIsSetter, BSTR *aRetValue, LONG64 *aRetTimestamp, BSTR *aRetFlags)
{
#ifdef VBOX_WITH_GUEST_PROPS
+# ifndef VBOX_COM_INPROC_API_CLIENT
AutoCaller autoCaller(this);
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
@@ -810,10 +934,10 @@ STDMETHODIMP Session::AccessGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR
return E_POINTER;
/* aValue can be NULL for a setter call if the property is to be deleted. */
if (aIsSetter && (aValue != NULL) && !VALID_PTR(aValue))
- return E_INVALIDARG;
+ return setError(E_INVALIDARG, tr("Invalid value pointer"));
/* aFlags can be null if it is to be left as is */
if (aIsSetter && (aFlags != NULL) && !VALID_PTR(aFlags))
- return E_INVALIDARG;
+ return setError(E_INVALIDARG, tr("Invalid flags pointer"));
/* If this session is not in a VM process fend off the call. The caller
* handles this correctly, by doing the operation in VBoxSVC. */
@@ -824,9 +948,17 @@ STDMETHODIMP Session::AccessGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR
return mConsole->getGuestProperty(aName, aRetValue, aRetTimestamp, aRetFlags);
else
return mConsole->setGuestProperty(aName, aValue, aFlags);
-#else /* VBOX_WITH_GUEST_PROPS not defined */
+
+# else /* VBOX_COM_INPROC_API_CLIENT */
+ /** @todo This is nonsense, non-VM API users shouldn't need to deal with this
+ * method call, VBoxSVC should be clever enough to see that the
+ * session doesn't have a console! */
+ return E_ACCESSDENIED;
+# endif /* VBOX_COM_INPROC_API_CLIENT */
+
+#else /* VBOX_WITH_GUEST_PROPS */
ReturnComNotImplemented();
-#endif /* VBOX_WITH_GUEST_PROPS not defined */
+#endif /* VBOX_WITH_GUEST_PROPS */
}
STDMETHODIMP Session::EnumerateGuestProperties(IN_BSTR aPatterns,
@@ -835,7 +967,7 @@ STDMETHODIMP Session::EnumerateGuestProperties(IN_BSTR aPatterns,
ComSafeArrayOut(LONG64, aTimestamps),
ComSafeArrayOut(BSTR, aFlags))
{
-#ifdef VBOX_WITH_GUEST_PROPS
+#if defined(VBOX_WITH_GUEST_PROPS) && !defined(VBOX_COM_INPROC_API_CLIENT)
AutoCaller autoCaller(this);
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
@@ -872,10 +1004,6 @@ STDMETHODIMP Session::EnumerateGuestProperties(IN_BSTR aPatterns,
STDMETHODIMP Session::OnlineMergeMedium(IMediumAttachment *aMediumAttachment,
ULONG aSourceIdx, ULONG aTargetIdx,
- IMedium *aSource, IMedium *aTarget,
- BOOL aMergeForward,
- IMedium *aParentForTarget,
- ComSafeArrayIn(IMedium *, aChildrenToReparent),
IProgress *aProgress)
{
AutoCaller autoCaller(this);
@@ -885,16 +1013,18 @@ STDMETHODIMP Session::OnlineMergeMedium(IMediumAttachment *aMediumAttachment,
return setError(VBOX_E_INVALID_VM_STATE,
tr("Machine is not locked by session (session state: %s)."),
Global::stringifySessionState(mState));
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
CheckComArgNotNull(aMediumAttachment);
- CheckComArgSafeArrayNotNull(aChildrenToReparent);
- return mConsole->onlineMergeMedium(aMediumAttachment, aSourceIdx,
- aTargetIdx, aSource, aTarget,
- aMergeForward, aParentForTarget,
- ComSafeArrayInArg(aChildrenToReparent),
+ return mConsole->onlineMergeMedium(aMediumAttachment,
+ aSourceIdx, aTargetIdx,
aProgress);
+#else
+ AssertFailed();
+ return E_NOTIMPL;
+#endif
}
STDMETHODIMP Session::EnableVMMStatistics(BOOL aEnable)
@@ -905,11 +1035,70 @@ STDMETHODIMP Session::EnableVMMStatistics(BOOL aEnable)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
mConsole->enableVMMStatistics(aEnable);
return S_OK;
+#else
+ AssertFailed();
+ return E_NOTIMPL;
+#endif
+}
+
+STDMETHODIMP Session::PauseWithReason(Reason_T aReason)
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
+ AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
+ AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
+
+ return mConsole->pause(aReason);
+#else
+ AssertFailed();
+ return E_NOTIMPL;
+#endif
+}
+
+STDMETHODIMP Session::ResumeWithReason(Reason_T aReason)
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
+ AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
+ AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
+
+ return mConsole->resume(aReason);
+#else
+ AssertFailed();
+ return E_NOTIMPL;
+#endif
+}
+
+STDMETHODIMP Session::SaveStateWithReason(Reason_T aReason, IProgress **aProgress)
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
+ AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
+#ifndef VBOX_COM_INPROC_API_CLIENT
+ AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
+
+ return mConsole->saveState(aReason, aProgress);
+#else
+ AssertFailed();
+ return E_NOTIMPL;
+#endif
}
// private methods
@@ -953,16 +1142,9 @@ HRESULT Session::unlockMachine(bool aFinalRelease, bool aFromServer)
mState = SessionState_Unlocked;
mType = SessionType_Null;
-#if defined(RT_OS_WINDOWS)
- Assert(!mIPCSem && !mIPCThreadSem);
-#elif defined(RT_OS_OS2)
- Assert(mIPCThread == NIL_RTTHREAD &&
- mIPCThreadSem == NIL_RTSEMEVENT);
-#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
- Assert(mIPCSem == -1);
-#else
-# error "Port me!"
-#endif
+
+ Assert(!mClientTokenHolder);
+
LogFlowThisFuncLeave();
return S_OK;
}
@@ -972,11 +1154,15 @@ HRESULT Session::unlockMachine(bool aFinalRelease, bool aFromServer)
if (mType == SessionType_WriteLock)
{
+#ifndef VBOX_COM_INPROC_API_CLIENT
if (!mConsole.isNull())
{
mConsole->uninit();
mConsole.setNull();
}
+#else
+ mRemoteMachine.setNull();
+#endif
}
else
{
@@ -1032,7 +1218,12 @@ HRESULT Session::unlockMachine(bool aFinalRelease, bool aFromServer)
if (mType == SessionType_WriteLock)
{
- releaseIPCSemaphore();
+ if (mClientTokenHolder)
+ {
+ delete mClientTokenHolder;
+ mClientTokenHolder = NULL;
+ }
+
if (!aFinalRelease && !aFromServer)
{
/*
@@ -1056,286 +1247,4 @@ HRESULT Session::unlockMachine(bool aFinalRelease, bool aFromServer)
return S_OK;
}
-/** @note To be called only from #AssignMachine() */
-HRESULT Session::grabIPCSemaphore()
-{
- HRESULT rc = E_FAIL;
-
- /* open the IPC semaphore based on the sessionId and try to grab it */
- Bstr ipcId;
- rc = mControl->GetIPCId(ipcId.asOutParam());
- AssertComRCReturnRC(rc);
-
- LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));
-
-#if defined(RT_OS_WINDOWS)
-
- /*
- * Since Session is an MTA object, this method can be executed on
- * any thread, and this thread will not necessarily match the thread on
- * which close() will be called later. Therefore, we need a separate
- * thread to hold the IPC mutex and then release it in close().
- */
-
- mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
- AssertMsgReturn(mIPCThreadSem,
- ("Cannot create an event sem, err=%d", ::GetLastError()),
- E_FAIL);
-
- void *data[3];
- data[0] = (void*)(BSTR)ipcId.raw();
- data[1] = (void*)mIPCThreadSem;
- data[2] = 0; /* will get an output from the thread */
-
- /* create a thread to hold the IPC mutex until signalled to release it */
- RTTHREAD tid;
- int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
- AssertRCReturn(vrc, E_FAIL);
-
- /* wait until thread init is completed */
- DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE);
- AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
- Assert(data[2]);
-
- if (wrc == WAIT_OBJECT_0 && data[2])
- {
- /* memorize the event sem we should signal in close() */
- mIPCSem = (HANDLE)data[2];
- rc = S_OK;
- }
- else
- {
- ::CloseHandle(mIPCThreadSem);
- mIPCThreadSem = NULL;
- rc = E_FAIL;
- }
-
-#elif defined(RT_OS_OS2)
-
- /* We use XPCOM where any message (including close()) can arrive on any
- * worker thread (which will not necessarily match this thread that opens
- * the mutex). Therefore, we need a separate thread to hold the IPC mutex
- * and then release it in close(). */
-
- int vrc = RTSemEventCreate(&mIPCThreadSem);
- AssertRCReturn(vrc, E_FAIL);
-
- void *data[3];
- data[0] = (void*)ipcId.raw();
- data[1] = (void*)mIPCThreadSem;
- data[2] = (void*)false; /* will get the thread result here */
-
- /* create a thread to hold the IPC mutex until signalled to release it */
- vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data,
- 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
- AssertRCReturn(vrc, E_FAIL);
-
- /* wait until thread init is completed */
- vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
- AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);
-
- /* the thread must succeed */
- AssertReturn((bool)data[2], E_FAIL);
-
-#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
-
-# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
- Utf8Str ipcKey = ipcId;
- key_t key = RTStrToUInt32(ipcKey.c_str());
- AssertMsgReturn (key != 0,
- ("Key value of 0 is not valid for IPC semaphore"),
- E_FAIL);
-# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
- Utf8Str semName = ipcId;
- char *pszSemName = NULL;
- RTStrUtf8ToCurrentCP (&pszSemName, semName);
- key_t key = ::ftok (pszSemName, 'V');
- RTStrFree (pszSemName);
-# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
-
- mIPCSem = ::semget (key, 0, 0);
- AssertMsgReturn (mIPCSem >= 0,
- ("Cannot open IPC semaphore, errno=%d", errno),
- E_FAIL);
-
- /* grab the semaphore */
- ::sembuf sop = { 0, -1, SEM_UNDO };
- int rv = ::semop (mIPCSem, &sop, 1);
- AssertMsgReturn (rv == 0,
- ("Cannot grab IPC semaphore, errno=%d", errno),
- E_FAIL);
-
-#else
-# error "Port me!"
-#endif
-
- return rc;
-}
-
-/** @note To be called only from #close() */
-void Session::releaseIPCSemaphore()
-{
- /* release the IPC semaphore */
-#if defined(RT_OS_WINDOWS)
-
- if (mIPCSem && mIPCThreadSem)
- {
- /*
- * tell the thread holding the IPC mutex to release it;
- * it will close mIPCSem handle
- */
- ::SetEvent (mIPCSem);
- /* wait for the thread to finish */
- ::WaitForSingleObject (mIPCThreadSem, INFINITE);
- ::CloseHandle (mIPCThreadSem);
-
- mIPCThreadSem = NULL;
- mIPCSem = NULL;
- }
-
-#elif defined(RT_OS_OS2)
-
- if (mIPCThread != NIL_RTTHREAD)
- {
- Assert (mIPCThreadSem != NIL_RTSEMEVENT);
-
- /* tell the thread holding the IPC mutex to release it */
- int vrc = RTSemEventSignal (mIPCThreadSem);
- AssertRC(vrc == NO_ERROR);
-
- /* wait for the thread to finish */
- vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);
- Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
-
- mIPCThread = NIL_RTTHREAD;
- }
-
- if (mIPCThreadSem != NIL_RTSEMEVENT)
- {
- RTSemEventDestroy (mIPCThreadSem);
- mIPCThreadSem = NIL_RTSEMEVENT;
- }
-
-#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
-
- if (mIPCSem >= 0)
- {
- ::sembuf sop = { 0, 1, SEM_UNDO };
- ::semop (mIPCSem, &sop, 1);
-
- mIPCSem = -1;
- }
-
-#else
-# error "Port me!"
-#endif
-}
-
-#if defined(RT_OS_WINDOWS)
-/** VM IPC mutex holder thread */
-DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
-{
- LogFlowFuncEnter();
-
- Assert (pvUser);
- void **data = (void **) pvUser;
-
- BSTR sessionId = (BSTR)data[0];
- HANDLE initDoneSem = (HANDLE)data[1];
-
- HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
- AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));
-
- if (ipcMutex)
- {
- /* grab the mutex */
- DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
- AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
- if (wrc == WAIT_OBJECT_0)
- {
- HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
- AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
- if (finishSem)
- {
- data[2] = (void*)finishSem;
- /* signal we're done with init */
- ::SetEvent (initDoneSem);
- /* wait until we're signaled to release the IPC mutex */
- ::WaitForSingleObject (finishSem, INFINITE);
- /* release the IPC mutex */
- LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
- BOOL success = ::ReleaseMutex (ipcMutex);
- AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
- ::CloseHandle (ipcMutex);
- ::CloseHandle (finishSem);
- }
- }
- }
-
- /* signal we're done */
- ::SetEvent (initDoneSem);
-
- LogFlowFuncLeave();
-
- return 0;
-}
-#endif
-
-#if defined(RT_OS_OS2)
-/** VM IPC mutex holder thread */
-DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
-{
- LogFlowFuncEnter();
-
- Assert (pvUser);
- void **data = (void **) pvUser;
-
- Utf8Str ipcId = (BSTR)data[0];
- RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
-
- LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));
-
- HMTX ipcMutex = NULLHANDLE;
- APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);
- AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));
-
- if (arc == NO_ERROR)
- {
- /* grab the mutex */
- LogFlowFunc (("grabbing IPC mutex...\n"));
- arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);
- AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));
- if (arc == NO_ERROR)
- {
- /* store the answer */
- data[2] = (void*)true;
- /* signal we're done */
- int vrc = RTThreadUserSignal (Thread);
- AssertRC(vrc);
-
- /* wait until we're signaled to release the IPC mutex */
- LogFlowFunc (("waiting for termination signal..\n"));
- vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);
- Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
-
- /* release the IPC mutex */
- LogFlowFunc (("releasing IPC mutex...\n"));
- arc = ::DosReleaseMutexSem (ipcMutex);
- AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));
- }
-
- ::DosCloseMutexSem (ipcMutex);
- }
-
- /* store the answer */
- data[1] = (void*)false;
- /* signal we're done */
- int vrc = RTThreadUserSignal (Thread);
- AssertRC(vrc);
-
- LogFlowFuncLeave();
-
- return 0;
-}
-#endif
/* vi: set tabstop=4 shiftwidth=4 expandtab: */
diff --git a/src/VBox/Main/src-client/USBDeviceImpl.cpp b/src/VBox/Main/src-client/USBDeviceImpl.cpp
index a927d64f..023b6979 100644
--- a/src/VBox/Main/src-client/USBDeviceImpl.cpp
+++ b/src/VBox/Main/src-client/USBDeviceImpl.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2008 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -82,10 +82,10 @@ HRESULT OUSBDevice::init(IUSBDevice *aUSBDevice)
hrc = aUSBDevice->COMGETTER(Port)(&unconst(mData.port));
ComAssertComRCRet(hrc, hrc);
- hrc = aUSBDevice->COMGETTER(Port)(&unconst(mData.version));
+ hrc = aUSBDevice->COMGETTER(Version)(&unconst(mData.version));
ComAssertComRCRet(hrc, hrc);
- hrc = aUSBDevice->COMGETTER(Port)(&unconst(mData.portVersion));
+ hrc = aUSBDevice->COMGETTER(PortVersion)(&unconst(mData.portVersion));
ComAssertComRCRet(hrc, hrc);
hrc = aUSBDevice->COMGETTER(Remote)(&unconst(mData.remote));
diff --git a/src/VBox/Main/src-client/UsbCardReader.cpp b/src/VBox/Main/src-client/UsbCardReader.cpp
index f7a7bc5b..477beeff 100644
--- a/src/VBox/Main/src-client/UsbCardReader.cpp
+++ b/src/VBox/Main/src-client/UsbCardReader.cpp
@@ -1,5 +1,4 @@
/* $Id: UsbCardReader.cpp $ */
-
/** @file
* UsbCardReader - Driver Interface to USB Smart Card Reader emulation.
*/
@@ -69,9 +68,9 @@ static DECLCALLBACK(void) drvCardReaderCmdStatusChange(PUSBCARDREADER pThis,
UsbCardReader *pUsbCardReader = pThis->pUsbCardReader;
if (!pUsbCardReader)
{
- pThis->pICardReaderUp->pfnCardReaderUpSetStatusChange(pThis->pICardReaderUp,
- pvUser, VRDE_SCARD_E_NO_SMARTCARD,
- paReaderStats, cReaderStats);
+ pThis->pICardReaderUp->pfnSetStatusChange(pThis->pICardReaderUp,
+ pvUser, VRDE_SCARD_E_NO_SMARTCARD,
+ paReaderStats, cReaderStats);
}
else
{
@@ -90,8 +89,8 @@ static DECLCALLBACK(void) drvCardReaderCmdEstablishContext(PUSBCARDREADER pThis)
UsbCardReader *pUsbCardReader = pThis->pUsbCardReader;
if (!pUsbCardReader)
{
- pThis->pICardReaderUp->pfnCardReaderUpEstablishContext(pThis->pICardReaderUp,
- VRDE_SCARD_E_NO_SMARTCARD);
+ pThis->pICardReaderUp->pfnEstablishContext(pThis->pICardReaderUp,
+ VRDE_SCARD_E_NO_SMARTCARD);
}
else
{
@@ -130,15 +129,15 @@ static DECLCALLBACK(void) drvCardReaderCmdStatus(PUSBCARDREADER pThis,
UsbCardReader *pUsbCardReader = pThis->pUsbCardReader;
if (!pUsbCardReader)
{
- pThis->pICardReaderUp->pfnCardReaderUpStatus(pThis->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_SMARTCARD,
- /* pszReaderName */ NULL,
- /* cchReaderName */ 0,
- /* u32CardState */ 0,
- /* u32Protocol */ 0,
- /* pu8Atr */ 0,
- /* cbAtr */ 0);
+ pThis->pICardReaderUp->pfnStatus(pThis->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_SMARTCARD,
+ /* pszReaderName */ NULL,
+ /* cchReaderName */ 0,
+ /* u32CardState */ 0,
+ /* u32Protocol */ 0,
+ /* pu8Atr */ 0,
+ /* cbAtr */ 0);
}
else
{
@@ -160,10 +159,10 @@ static DECLCALLBACK(void) drvCardReaderCmdConnect(PUSBCARDREADER pThis,
UsbCardReader *pUsbCardReader = pThis->pUsbCardReader;
if (!pUsbCardReader)
{
- pThis->pICardReaderUp->pfnCardReaderUpConnect(pThis->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_SMARTCARD,
- 0);
+ pThis->pICardReaderUp->pfnConnect(pThis->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_SMARTCARD,
+ 0);
}
else
{
@@ -184,9 +183,9 @@ static DECLCALLBACK(void) drvCardReaderCmdDisconnect(PUSBCARDREADER pThis,
UsbCardReader *pUsbCardReader = pThis->pUsbCardReader;
if (!pUsbCardReader)
{
- pThis->pICardReaderUp->pfnCardReaderUpDisconnect(pThis->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_SMARTCARD);
+ pThis->pICardReaderUp->pfnDisconnect(pThis->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_SMARTCARD);
}
else
{
@@ -209,12 +208,12 @@ static DECLCALLBACK(void) drvCardReaderCmdTransmit(PUSBCARDREADER pThis,
UsbCardReader *pUsbCardReader = pThis->pUsbCardReader;
if (!pUsbCardReader)
{
- pThis->pICardReaderUp->pfnCardReaderUpTransmit(pThis->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_SMARTCARD,
- /* pioRecvPci */ NULL,
- /* pu8RecvBuffer */ NULL,
- /* cbRecvBuffer*/ 0);
+ pThis->pICardReaderUp->pfnTransmit(pThis->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_SMARTCARD,
+ /* pioRecvPci */ NULL,
+ /* pu8RecvBuffer */ NULL,
+ /* cbRecvBuffer*/ 0);
}
else
{
@@ -240,12 +239,12 @@ static DECLCALLBACK(void) drvCardReaderCmdGetAttr(PUSBCARDREADER pThis,
UsbCardReader *pUsbCardReader = pThis->pUsbCardReader;
if (!pUsbCardReader)
{
- pThis->pICardReaderUp->pfnCardReaderUpGetAttrib(pThis->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_SMARTCARD,
- u32AttrId,
- /* pvAttrib */ NULL,
- /* cbAttrib */ 0);
+ pThis->pICardReaderUp->pfnGetAttrib(pThis->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_SMARTCARD,
+ u32AttrId,
+ /* pvAttrib */ NULL,
+ /* cbAttrib */ 0);
}
else
{
@@ -267,10 +266,10 @@ static DECLCALLBACK(void) drvCardReaderCmdSetAttr(PUSBCARDREADER pThis,
UsbCardReader *pUsbCardReader = pThis->pUsbCardReader;
if (!pUsbCardReader)
{
- pThis->pICardReaderUp->pfnCardReaderUpSetAttrib(pThis->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_SMARTCARD,
- u32AttrId);
+ pThis->pICardReaderUp->pfnSetAttrib(pThis->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_SMARTCARD,
+ u32AttrId);
}
else
{
@@ -296,12 +295,12 @@ static DECLCALLBACK(void) drvCardReaderCmdControl(PUSBCARDREADER pThis,
UsbCardReader *pUsbCardReader = pThis->pUsbCardReader;
if (!pUsbCardReader)
{
- pThis->pICardReaderUp->pfnCardReaderUpControl(pThis->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_SMARTCARD,
- u32ControlCode,
- /* pvOutBuffer */ NULL,
- /* cbOutBuffer */ 0);
+ pThis->pICardReaderUp->pfnControl(pThis->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_SMARTCARD,
+ u32ControlCode,
+ /* pvOutBuffer */ NULL,
+ /* cbOutBuffer */ 0);
}
else
{
@@ -891,11 +890,11 @@ int UsbCardReader::VRDEResponse(int rcRequest, void *pvUser, uint32_t u32Functio
}
}
- mpDrv->pICardReaderUp->pfnCardReaderUpSetStatusChange(mpDrv->pICardReaderUp,
- pCtx->pvUser,
- rcCard,
- pCtx->u.GetStatusChange.paReaderStats,
- pCtx->u.GetStatusChange.cReaderStats);
+ mpDrv->pICardReaderUp->pfnSetStatusChange(mpDrv->pICardReaderUp,
+ pCtx->pvUser,
+ rcCard,
+ pCtx->u.GetStatusChange.paReaderStats,
+ pCtx->u.GetStatusChange.cReaderStats);
RTMemFree(pCtx);
} break;
@@ -942,10 +941,10 @@ int UsbCardReader::VRDEResponse(int rcRequest, void *pvUser, uint32_t u32Functio
}
}
- mpDrv->pICardReaderUp->pfnCardReaderUpConnect(mpDrv->pICardReaderUp,
- pCtx->pvUser,
- rcCard,
- u32ActiveProtocol);
+ mpDrv->pICardReaderUp->pfnConnect(mpDrv->pICardReaderUp,
+ pCtx->pvUser,
+ rcCard,
+ u32ActiveProtocol);
RTMemFree(pCtx);
} break;
@@ -984,9 +983,9 @@ int UsbCardReader::VRDEResponse(int rcRequest, void *pvUser, uint32_t u32Functio
rcCard = pRsp->u32ReturnCode;
}
- mpDrv->pICardReaderUp->pfnCardReaderUpDisconnect(mpDrv->pICardReaderUp,
- pCtx->pvUser,
- rcCard);
+ mpDrv->pICardReaderUp->pfnDisconnect(mpDrv->pICardReaderUp,
+ pCtx->pvUser,
+ rcCard);
RTMemFree(pCtx);
} break;
@@ -1054,7 +1053,7 @@ int UsbCardReader::VRDEResponse(int rcRequest, void *pvUser, uint32_t u32Functio
if (pRsp->u32ReturnCode == VRDE_SCARD_S_SUCCESS)
{
pszReaderName = pRsp->szReader;
- cchReaderName = strlen(pRsp->szReader) + 1;
+ cchReaderName = (uint32_t)strlen(pRsp->szReader) + 1;
u32CardState = pRsp->u32State;
u32Protocol = pRsp->u32Protocol;
u32AtrLength = pRsp->u32AtrLength;
@@ -1062,15 +1061,15 @@ int UsbCardReader::VRDEResponse(int rcRequest, void *pvUser, uint32_t u32Functio
}
}
- mpDrv->pICardReaderUp->pfnCardReaderUpStatus(mpDrv->pICardReaderUp,
- pCtx->pvUser,
- rcCard,
- pszReaderName,
- cchReaderName,
- u32CardState,
- u32Protocol,
- pbAtr,
- u32AtrLength);
+ mpDrv->pICardReaderUp->pfnStatus(mpDrv->pICardReaderUp,
+ pCtx->pvUser,
+ rcCard,
+ pszReaderName,
+ cchReaderName,
+ u32CardState,
+ u32Protocol,
+ pbAtr,
+ u32AtrLength);
RTMemFree(pCtx);
} break;
@@ -1107,12 +1106,12 @@ int UsbCardReader::VRDEResponse(int rcRequest, void *pvUser, uint32_t u32Functio
}
}
- mpDrv->pICardReaderUp->pfnCardReaderUpTransmit(mpDrv->pICardReaderUp,
- pCtx->pvUser,
- rcCard,
- pioRecvPci,
- pu8RecvBuffer,
- cbRecvBuffer);
+ mpDrv->pICardReaderUp->pfnTransmit(mpDrv->pICardReaderUp,
+ pCtx->pvUser,
+ rcCard,
+ pioRecvPci,
+ pu8RecvBuffer,
+ cbRecvBuffer);
RTMemFree(pioRecvPci);
@@ -1149,12 +1148,12 @@ int UsbCardReader::VRDEResponse(int rcRequest, void *pvUser, uint32_t u32Functio
}
}
- mpDrv->pICardReaderUp->pfnCardReaderUpControl(mpDrv->pICardReaderUp,
- pCtx->pvUser,
- rcCard,
- pCtx->u.Control.u32ControlCode,
- pu8OutBuffer,
- cbOutBuffer);
+ mpDrv->pICardReaderUp->pfnControl(mpDrv->pICardReaderUp,
+ pCtx->pvUser,
+ rcCard,
+ pCtx->u.Control.u32ControlCode,
+ pu8OutBuffer,
+ cbOutBuffer);
RTMemFree(pCtx);
} break;
@@ -1189,12 +1188,12 @@ int UsbCardReader::VRDEResponse(int rcRequest, void *pvUser, uint32_t u32Functio
}
}
- mpDrv->pICardReaderUp->pfnCardReaderUpGetAttrib(mpDrv->pICardReaderUp,
- pCtx->pvUser,
- rcCard,
- pCtx->u.GetAttrib.u32AttrId,
- pu8Attrib,
- cbAttrib);
+ mpDrv->pICardReaderUp->pfnGetAttrib(mpDrv->pICardReaderUp,
+ pCtx->pvUser,
+ rcCard,
+ pCtx->u.GetAttrib.u32AttrId,
+ pu8Attrib,
+ cbAttrib);
RTMemFree(pCtx);
} break;
@@ -1220,10 +1219,10 @@ int UsbCardReader::VRDEResponse(int rcRequest, void *pvUser, uint32_t u32Functio
rcCard = pRsp->u32ReturnCode;
}
- mpDrv->pICardReaderUp->pfnCardReaderUpSetAttrib(mpDrv->pICardReaderUp,
- pCtx->pvUser,
- rcCard,
- pCtx->u.SetAttrib.u32AttrId);
+ mpDrv->pICardReaderUp->pfnSetAttrib(mpDrv->pICardReaderUp,
+ pCtx->pvUser,
+ rcCard,
+ pCtx->u.SetAttrib.u32AttrId);
RTMemFree(pCtx);
} break;
@@ -1245,8 +1244,8 @@ int UsbCardReader::EstablishContext(struct USBCARDREADER *pDrv)
* The device can be detached at the moment, for example the VRDP client did not connect yet.
*/
- return mpDrv->pICardReaderUp->pfnCardReaderUpEstablishContext(mpDrv->pICardReaderUp,
- VRDE_SCARD_S_SUCCESS);
+ return mpDrv->pICardReaderUp->pfnEstablishContext(mpDrv->pICardReaderUp,
+ VRDE_SCARD_S_SUCCESS);
}
int UsbCardReader::ReleaseContext(struct USBCARDREADER *pDrv)
@@ -1306,22 +1305,22 @@ int UsbCardReader::GetStatusChange(struct USBCARDREADER *pDrv,
|| !m_pRemote->fContext
|| !m_pRemote->reader.fAvailable)
{
- rc = mpDrv->pICardReaderUp->pfnCardReaderUpSetStatusChange(mpDrv->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_SMARTCARD,
- paReaderStats,
- cReaderStats);
+ rc = mpDrv->pICardReaderUp->pfnSetStatusChange(mpDrv->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_SMARTCARD,
+ paReaderStats,
+ cReaderStats);
}
else
{
UCRREQCTX *pCtx = (UCRREQCTX *)RTMemAlloc(sizeof(UCRREQCTX));
if (!pCtx)
{
- rc = mpDrv->pICardReaderUp->pfnCardReaderUpSetStatusChange(mpDrv->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_MEMORY,
- paReaderStats,
- cReaderStats);
+ rc = mpDrv->pICardReaderUp->pfnSetStatusChange(mpDrv->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_MEMORY,
+ paReaderStats,
+ cReaderStats);
}
else
{
@@ -1364,20 +1363,20 @@ int UsbCardReader::Connect(struct USBCARDREADER *pDrv,
|| !m_pRemote->fContext
|| !m_pRemote->reader.fAvailable)
{
- rc = mpDrv->pICardReaderUp->pfnCardReaderUpConnect(mpDrv->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_SMARTCARD,
- VRDE_SCARD_PROTOCOL_T0);
+ rc = mpDrv->pICardReaderUp->pfnConnect(mpDrv->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_SMARTCARD,
+ VRDE_SCARD_PROTOCOL_T0);
}
else
{
UCRREQCTX *pCtx = (UCRREQCTX *)RTMemAlloc(sizeof(UCRREQCTX));
if (!pCtx)
{
- rc = mpDrv->pICardReaderUp->pfnCardReaderUpConnect(mpDrv->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_MEMORY,
- VRDE_SCARD_PROTOCOL_T0);
+ rc = mpDrv->pICardReaderUp->pfnConnect(mpDrv->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_MEMORY,
+ VRDE_SCARD_PROTOCOL_T0);
}
else
{
@@ -1416,18 +1415,18 @@ int UsbCardReader::Disconnect(struct USBCARDREADER *pDrv,
|| !m_pRemote->reader.fAvailable
|| !m_pRemote->reader.fHandle)
{
- rc = mpDrv->pICardReaderUp->pfnCardReaderUpDisconnect(mpDrv->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_SMARTCARD);
+ rc = mpDrv->pICardReaderUp->pfnDisconnect(mpDrv->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_SMARTCARD);
}
else
{
UCRREQCTX *pCtx = (UCRREQCTX *)RTMemAlloc(sizeof(UCRREQCTX));
if (!pCtx)
{
- rc = mpDrv->pICardReaderUp->pfnCardReaderUpDisconnect(mpDrv->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_MEMORY);
+ rc = mpDrv->pICardReaderUp->pfnDisconnect(mpDrv->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_MEMORY);
}
else
{
@@ -1467,30 +1466,30 @@ int UsbCardReader::Status(struct USBCARDREADER *pDrv,
|| !m_pRemote->reader.fAvailable
|| !m_pRemote->reader.fHandle)
{
- rc = mpDrv->pICardReaderUp->pfnCardReaderUpStatus(mpDrv->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_SMARTCARD,
- /* pszReaderName */ NULL,
- /* cchReaderName */ 0,
- /* u32CardState */ 0,
- /* u32Protocol */ 0,
- /* pu8Atr */ 0,
- /* cbAtr */ 0);
+ rc = mpDrv->pICardReaderUp->pfnStatus(mpDrv->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_SMARTCARD,
+ /* pszReaderName */ NULL,
+ /* cchReaderName */ 0,
+ /* u32CardState */ 0,
+ /* u32Protocol */ 0,
+ /* pu8Atr */ 0,
+ /* cbAtr */ 0);
}
else
{
UCRREQCTX *pCtx = (UCRREQCTX *)RTMemAlloc(sizeof(UCRREQCTX));
if (!pCtx)
{
- rc = mpDrv->pICardReaderUp->pfnCardReaderUpStatus(mpDrv->pICardReaderUp,
- pvUser,
- VRDE_SCARD_E_NO_MEMORY,
- /* pszReaderName */ NULL,
- /* cchReaderName */ 0,
- /* u32CardState */ 0,
- /* u32Protocol */ 0,
- /* pu8Atr */ 0,
- /* cbAtr */ 0);
+ rc = mpDrv->pICardReaderUp->pfnStatus(mpDrv->pICardReaderUp,
+ pvUser,
+ VRDE_SCARD_E_NO_MEMORY,
+ /* pszReaderName */ NULL,
+ /* cchReaderName */ 0,
+ /* u32CardState */ 0,
+ /* u32Protocol */ 0,
+ /* pu8Atr */ 0,
+ /* cbAtr */ 0);
}
else
{
@@ -1560,12 +1559,12 @@ int UsbCardReader::Transmit(struct USBCARDREADER *pDrv,
{
Assert(pCtx == NULL);
- rc = pDrv->pICardReaderUp->pfnCardReaderUpTransmit(pDrv->pICardReaderUp,
- pvUser,
- rcSCard,
- /* pioRecvPci */ NULL,
- /* pu8RecvBuffer */ NULL,
- /* cbRecvBuffer*/ 0);
+ rc = pDrv->pICardReaderUp->pfnTransmit(pDrv->pICardReaderUp,
+ pvUser,
+ rcSCard,
+ /* pioRecvPci */ NULL,
+ /* pu8RecvBuffer */ NULL,
+ /* cbRecvBuffer*/ 0);
}
else
{
@@ -1645,12 +1644,12 @@ int UsbCardReader::Control(struct USBCARDREADER *pDrv,
{
Assert(pCtx == NULL);
- rc = pDrv->pICardReaderUp->pfnCardReaderUpControl(pDrv->pICardReaderUp,
- pvUser,
- rcSCard,
- u32ControlCode,
- /* pvOutBuffer */ NULL,
- /* cbOutBuffer*/ 0);
+ rc = pDrv->pICardReaderUp->pfnControl(pDrv->pICardReaderUp,
+ pvUser,
+ rcSCard,
+ u32ControlCode,
+ /* pvOutBuffer */ NULL,
+ /* cbOutBuffer*/ 0);
}
else
{
@@ -1719,12 +1718,12 @@ int UsbCardReader::GetAttrib(struct USBCARDREADER *pDrv,
{
Assert(pCtx == NULL);
- pDrv->pICardReaderUp->pfnCardReaderUpGetAttrib(pDrv->pICardReaderUp,
- pvUser,
- rcSCard,
- u32AttrId,
- /* pvAttrib */ NULL,
- /* cbAttrib */ 0);
+ pDrv->pICardReaderUp->pfnGetAttrib(pDrv->pICardReaderUp,
+ pvUser,
+ rcSCard,
+ u32AttrId,
+ /* pvAttrib */ NULL,
+ /* cbAttrib */ 0);
}
else
{
@@ -1792,10 +1791,10 @@ int UsbCardReader::SetAttrib(struct USBCARDREADER *pDrv,
{
Assert(pCtx == NULL);
- pDrv->pICardReaderUp->pfnCardReaderUpSetAttrib(pDrv->pICardReaderUp,
- pvUser,
- rcSCard,
- u32AttrId);
+ pDrv->pICardReaderUp->pfnSetAttrib(pDrv->pICardReaderUp,
+ pvUser,
+ rcSCard,
+ u32AttrId);
}
else
{
@@ -1823,7 +1822,7 @@ int UsbCardReader::SetAttrib(struct USBCARDREADER *pDrv,
/*
- * PDM
+ * PDMDRVINS
*/
/* static */ DECLCALLBACK(void *) UsbCardReader::drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
@@ -1837,11 +1836,38 @@ int UsbCardReader::SetAttrib(struct USBCARDREADER *pDrv,
return NULL;
}
+/* static */ DECLCALLBACK(void) UsbCardReader::drvDestruct(PPDMDRVINS pDrvIns)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ LogFlowFunc(("iInstance/%d\n",pDrvIns->iInstance));
+ PUSBCARDREADER pThis = PDMINS_2_DATA(pDrvIns, PUSBCARDREADER);
+
+ /** @todo The driver is destroyed before the device.
+ * So device calls ReleaseContext when there is no more driver.
+ * Notify the device here so it can do cleanup or
+ * do a cleanup now in the driver.
+ */
+ if (pThis->hReqQCardReaderCmd != NIL_RTREQQUEUE)
+ {
+ int rc = RTReqQueueDestroy(pThis->hReqQCardReaderCmd);
+ AssertRC(rc);
+ pThis->hReqQCardReaderCmd = NIL_RTREQQUEUE;
+ }
+
+ /** @todo r=bird: why doesn't this set pThis->pUsbCardReader->mpDrv to NULL like
+ * everyone else? */
+ pThis->pUsbCardReader = NULL;
+ LogFlowFuncLeave();
+}
+
/* static */ DECLCALLBACK(int) UsbCardReader::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
LogFlowFunc(("iInstance/%d, pCfg:%p, fFlags:%x\n", pDrvIns->iInstance, pCfg, fFlags));
PUSBCARDREADER pThis = PDMINS_2_DATA(pDrvIns, PUSBCARDREADER);
+ pThis->hReqQCardReaderCmd = NIL_RTREQQUEUE;
+
if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
@@ -1858,18 +1884,18 @@ int UsbCardReader::SetAttrib(struct USBCARDREADER *pDrv,
pDrvIns->IBase.pfnQueryInterface = UsbCardReader::drvQueryInterface;
- pThis->ICardReaderDown.pfnCardReaderDownEstablishContext = drvCardReaderDownEstablishContext;
- pThis->ICardReaderDown.pfnCardReaderDownReleaseContext = drvCardReaderDownReleaseContext;
- pThis->ICardReaderDown.pfnCardReaderDownConnect = drvCardReaderDownConnect;
- pThis->ICardReaderDown.pfnCardReaderDownDisconnect = drvCardReaderDownDisconnect;
- pThis->ICardReaderDown.pfnCardReaderDownStatus = drvCardReaderDownStatus;
- pThis->ICardReaderDown.pfnCardReaderDownGetStatusChange = drvCardReaderDownGetStatusChange;
- pThis->ICardReaderDown.pfnCardReaderDownBeginTransaction = drvCardReaderDownBeginTransaction;
- pThis->ICardReaderDown.pfnCardReaderDownEndTransaction = drvCardReaderDownEndTransaction;
- pThis->ICardReaderDown.pfnCardReaderDownTransmit = drvCardReaderDownTransmit;
- pThis->ICardReaderDown.pfnCardReaderDownGetAttr = drvCardReaderDownGetAttr;
- pThis->ICardReaderDown.pfnCardReaderDownSetAttr = drvCardReaderDownSetAttr;
- pThis->ICardReaderDown.pfnCardReaderDownControl = drvCardReaderDownControl;
+ pThis->ICardReaderDown.pfnEstablishContext = drvCardReaderDownEstablishContext;
+ pThis->ICardReaderDown.pfnReleaseContext = drvCardReaderDownReleaseContext;
+ pThis->ICardReaderDown.pfnConnect = drvCardReaderDownConnect;
+ pThis->ICardReaderDown.pfnDisconnect = drvCardReaderDownDisconnect;
+ pThis->ICardReaderDown.pfnStatus = drvCardReaderDownStatus;
+ pThis->ICardReaderDown.pfnGetStatusChange = drvCardReaderDownGetStatusChange;
+ pThis->ICardReaderDown.pfnBeginTransaction = drvCardReaderDownBeginTransaction;
+ pThis->ICardReaderDown.pfnEndTransaction = drvCardReaderDownEndTransaction;
+ pThis->ICardReaderDown.pfnTransmit = drvCardReaderDownTransmit;
+ pThis->ICardReaderDown.pfnGetAttr = drvCardReaderDownGetAttr;
+ pThis->ICardReaderDown.pfnSetAttr = drvCardReaderDownSetAttr;
+ pThis->ICardReaderDown.pfnControl = drvCardReaderDownControl;
pThis->pICardReaderUp = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICARDREADERUP);
AssertReturn(pThis->pICardReaderUp, VERR_PDM_MISSING_INTERFACE);
@@ -1894,27 +1920,6 @@ int UsbCardReader::SetAttrib(struct USBCARDREADER *pDrv,
return rc;
}
-/* static */ DECLCALLBACK(void) UsbCardReader::drvDestruct(PPDMDRVINS pDrvIns)
-{
- LogFlowFunc(("iInstance/%d\n",pDrvIns->iInstance));
- PUSBCARDREADER pThis = PDMINS_2_DATA(pDrvIns, PUSBCARDREADER);
-
- /* @todo The driver is destroyed before the device.
- * So device calls ReleaseContext when there is no more driver.
- * Notify the device here so it can do cleanup or
- * do a cleanup now in the driver.
- */
- if (pThis->hReqQCardReaderCmd != NIL_RTREQQUEUE)
- {
- int rc = RTReqQueueDestroy(pThis->hReqQCardReaderCmd);
- AssertRC(rc);
- pThis->hReqQCardReaderCmd = NIL_RTREQQUEUE;
- }
-
- pThis->pUsbCardReader = NULL;
- LogFlowFuncLeave();
-}
-
/* static */ const PDMDRVREG UsbCardReader::DrvReg =
{
/* u32Version */
diff --git a/src/VBox/Main/src-client/UsbWebcamInterface.cpp b/src/VBox/Main/src-client/UsbWebcamInterface.cpp
new file mode 100644
index 00000000..5a924eb4
--- /dev/null
+++ b/src/VBox/Main/src-client/UsbWebcamInterface.cpp
@@ -0,0 +1,472 @@
+/* $Id: UsbWebcamInterface.cpp $ */
+/** @file
+ * UsbWebcamInterface - Driver Interface for USB Webcam emulation.
+ */
+
+/*
+ * Copyright (C) 2011-2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+#define LOG_GROUP LOG_GROUP_USB_WEBCAM
+#include "UsbWebcamInterface.h"
+#include "ConsoleImpl.h"
+#include "ConsoleVRDPServer.h"
+#include "EmulatedUSBImpl.h"
+
+#include <VBox/vmm/pdmwebcaminfs.h>
+
+
+typedef struct EMWEBCAMREMOTE
+{
+ EmWebcam *pEmWebcam;
+
+ VRDEVIDEOINDEVICEHANDLE deviceHandle; /* The remote identifier. */
+
+ /* Received from the remote client. */
+ uint32_t u32Version; /* VRDE_VIDEOIN_NEGOTIATE_VERSION */
+ uint32_t fu32Capabilities; /* VRDE_VIDEOIN_NEGOTIATE_CAP_* */
+ VRDEVIDEOINDEVICEDESC *pDeviceDesc;
+ uint32_t cbDeviceDesc;
+
+ /* The device identifier for the PDM device.*/
+ uint64_t u64DeviceId;
+} EMWEBCAMREMOTE;
+
+typedef struct EMWEBCAMDRV
+{
+ EMWEBCAMREMOTE *pRemote;
+ PPDMIWEBCAMUP pIWebcamUp;
+ PDMIWEBCAMDOWN IWebcamDown;
+} EMWEBCAMDRV, *PEMWEBCAMDRV;
+
+typedef struct EMWEBCAMREQCTX
+{
+ EMWEBCAMREMOTE *pRemote;
+ void *pvUser;
+} EMWEBCAMREQCTX;
+
+
+static DECLCALLBACK(void) drvEmWebcamReady(PPDMIWEBCAMDOWN pInterface,
+ bool fReady)
+{
+ NOREF(fReady);
+
+ PEMWEBCAMDRV pThis = RT_FROM_MEMBER(pInterface, EMWEBCAMDRV, IWebcamDown);
+ EMWEBCAMREMOTE *pRemote = pThis->pRemote;
+
+ LogFlowFunc(("pRemote:%p\n", pThis->pRemote));
+
+ if (pThis->pIWebcamUp)
+ {
+ pThis->pIWebcamUp->pfnWebcamUpAttached(pThis->pIWebcamUp,
+ pRemote->u64DeviceId,
+ (const PDMIWEBCAM_DEVICEDESC *)pRemote->pDeviceDesc,
+ pRemote->cbDeviceDesc,
+ pRemote->u32Version,
+ pRemote->fu32Capabilities);
+ }
+}
+
+static DECLCALLBACK(int) drvEmWebcamControl(PPDMIWEBCAMDOWN pInterface,
+ void *pvUser,
+ uint64_t u64DeviceId,
+ const PDMIWEBCAM_CTRLHDR *pCtrl,
+ uint32_t cbCtrl)
+{
+ PEMWEBCAMDRV pThis = RT_FROM_MEMBER(pInterface, EMWEBCAMDRV, IWebcamDown);
+ EMWEBCAMREMOTE *pRemote = pThis->pRemote;
+
+ LogFlowFunc(("pRemote:%p, u64DeviceId %lld\n", pRemote, u64DeviceId));
+
+ return pRemote->pEmWebcam->SendControl(pThis, pvUser, u64DeviceId, (const VRDEVIDEOINCTRLHDR *)pCtrl, cbCtrl);
+}
+
+
+EmWebcam::EmWebcam(ConsoleVRDPServer *pServer)
+ :
+ mParent(pServer),
+ mpDrv(NULL),
+ mpRemote(NULL),
+ mu64DeviceIdSrc(0)
+{
+}
+
+EmWebcam::~EmWebcam()
+{
+ if (mpDrv)
+ {
+ mpDrv->pRemote = NULL;
+ mpDrv = NULL;
+ }
+}
+
+void EmWebcam::EmWebcamConstruct(EMWEBCAMDRV *pDrv)
+{
+ AssertReturnVoid(mpDrv == NULL);
+
+ mpDrv = pDrv;
+}
+
+void EmWebcam::EmWebcamDestruct(EMWEBCAMDRV *pDrv)
+{
+ AssertReturnVoid(pDrv == mpDrv);
+
+ if (mpRemote)
+ {
+ mParent->VideoInDeviceDetach(&mpRemote->deviceHandle);
+
+ RTMemFree(mpRemote->pDeviceDesc);
+ mpRemote->pDeviceDesc = NULL;
+ mpRemote->cbDeviceDesc = 0;
+
+ RTMemFree(mpRemote);
+ mpRemote = NULL;
+ }
+
+ mpDrv->pRemote = NULL;
+ mpDrv = NULL;
+}
+
+void EmWebcam::EmWebcamCbNotify(uint32_t u32Id, const void *pvData, uint32_t cbData)
+{
+ int rc = VINF_SUCCESS;
+
+ switch (u32Id)
+ {
+ case VRDE_VIDEOIN_NOTIFY_ID_ATTACH:
+ {
+ VRDEVIDEOINNOTIFYATTACH *p = (VRDEVIDEOINNOTIFYATTACH *)pvData;
+
+ /* Older versions did not report u32Version and fu32Capabilities. */
+ uint32_t u32Version = 1;
+ uint32_t fu32Capabilities = VRDE_VIDEOIN_NEGOTIATE_CAP_VOID;
+
+ if (cbData >= RT_OFFSETOF(VRDEVIDEOINNOTIFYATTACH, u32Version) + sizeof(p->u32Version))
+ {
+ u32Version = p->u32Version;
+ }
+
+ if (cbData >= RT_OFFSETOF(VRDEVIDEOINNOTIFYATTACH, fu32Capabilities) + sizeof(p->fu32Capabilities))
+ {
+ fu32Capabilities = p->fu32Capabilities;
+ }
+
+ LogFlowFunc(("ATTACH[%d,%d] version %d, caps 0x%08X\n",
+ p->deviceHandle.u32ClientId, p->deviceHandle.u32DeviceId,
+ u32Version, fu32Capabilities));
+
+ /* Currently only one device is allowed. */
+ if (mpRemote)
+ {
+ AssertFailed();
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ EMWEBCAMREMOTE *pRemote = (EMWEBCAMREMOTE *)RTMemAllocZ(sizeof(EMWEBCAMREMOTE));
+ if (pRemote == NULL)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ pRemote->pEmWebcam = this;
+ pRemote->deviceHandle = p->deviceHandle;
+ pRemote->u32Version = u32Version;
+ pRemote->fu32Capabilities = fu32Capabilities;
+ pRemote->pDeviceDesc = NULL;
+ pRemote->cbDeviceDesc = 0;
+ pRemote->u64DeviceId = ASMAtomicIncU64(&mu64DeviceIdSrc);
+
+ mpRemote = pRemote;
+
+ /* Tell the server that this webcam will be used. */
+ rc = mParent->VideoInDeviceAttach(&mpRemote->deviceHandle, mpRemote);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(mpRemote);
+ mpRemote = NULL;
+ break;
+ }
+
+ /* Get the device description. */
+ rc = mParent->VideoInGetDeviceDesc(NULL, &mpRemote->deviceHandle);
+
+ if (RT_FAILURE(rc))
+ {
+ mParent->VideoInDeviceDetach(&mpRemote->deviceHandle);
+ RTMemFree(mpRemote);
+ mpRemote = NULL;
+ break;
+ }
+
+ LogFlowFunc(("sent DeviceDesc\n"));
+ } break;
+
+ case VRDE_VIDEOIN_NOTIFY_ID_DETACH:
+ {
+ VRDEVIDEOINNOTIFYDETACH *p = (VRDEVIDEOINNOTIFYDETACH *)pvData;
+ Assert(cbData == sizeof(VRDEVIDEOINNOTIFYDETACH));
+
+ LogFlowFunc(("DETACH[%d,%d]\n", p->deviceHandle.u32ClientId, p->deviceHandle.u32DeviceId));
+
+ /* @todo */
+ if (mpRemote)
+ {
+ if (mpDrv && mpDrv->pIWebcamUp)
+ {
+ mpDrv->pIWebcamUp->pfnWebcamUpDetached(mpDrv->pIWebcamUp,
+ mpRemote->u64DeviceId);
+ }
+ /* mpRemote is deallocated in EmWebcamDestruct */
+ }
+ } break;
+
+ default:
+ rc = VERR_INVALID_PARAMETER;
+ AssertFailed();
+ break;
+ }
+
+ return;
+}
+
+void EmWebcam::EmWebcamCbDeviceDesc(int rcRequest, void *pDeviceCtx, void *pvUser,
+ const VRDEVIDEOINDEVICEDESC *pDeviceDesc, uint32_t cbDeviceDesc)
+{
+ EMWEBCAMREMOTE *pRemote = (EMWEBCAMREMOTE *)pDeviceCtx;
+ Assert(pRemote == mpRemote);
+
+ LogFlowFunc(("mpDrv %p, rcRequest %Rrc %p %p %p %d\n",
+ mpDrv, rcRequest, pDeviceCtx, pvUser, pDeviceDesc, cbDeviceDesc));
+
+ if (RT_SUCCESS(rcRequest))
+ {
+ /* Save device description. */
+ Assert(pRemote->pDeviceDesc == NULL);
+ pRemote->pDeviceDesc = (VRDEVIDEOINDEVICEDESC *)RTMemDup(pDeviceDesc, cbDeviceDesc);
+ pRemote->cbDeviceDesc = cbDeviceDesc;
+
+ /* Try to attach the device. */
+ EmulatedUSB *pEUSB = mParent->getConsole()->getEmulatedUSB();
+ pEUSB->webcamAttachInternal("", "", "EmWebcam", pRemote);
+ }
+ else
+ {
+ mParent->VideoInDeviceDetach(&mpRemote->deviceHandle);
+ RTMemFree(mpRemote);
+ mpRemote = NULL;
+ }
+}
+
+void EmWebcam::EmWebcamCbControl(int rcRequest, void *pDeviceCtx, void *pvUser,
+ const VRDEVIDEOINCTRLHDR *pControl, uint32_t cbControl)
+{
+ EMWEBCAMREMOTE *pRemote = (EMWEBCAMREMOTE *)pDeviceCtx;
+ Assert(pRemote == mpRemote);
+
+ LogFlowFunc(("rcRequest %Rrc %p %p %p %d\n",
+ rcRequest, pDeviceCtx, pvUser, pControl, cbControl));
+
+ bool fResponse = (pvUser != NULL);
+
+ if (mpDrv && mpDrv->pIWebcamUp)
+ {
+ mpDrv->pIWebcamUp->pfnWebcamUpControl(mpDrv->pIWebcamUp,
+ fResponse,
+ pvUser,
+ mpRemote->u64DeviceId,
+ (const PDMIWEBCAM_CTRLHDR *)pControl,
+ cbControl);
+ }
+
+ RTMemFree(pvUser);
+}
+
+void EmWebcam::EmWebcamCbFrame(int rcRequest, void *pDeviceCtx,
+ const VRDEVIDEOINPAYLOADHDR *pFrame, uint32_t cbFrame)
+{
+ LogFlowFunc(("rcRequest %Rrc %p %p %d\n",
+ rcRequest, pDeviceCtx, pFrame, cbFrame));
+
+ if (mpDrv && mpDrv->pIWebcamUp)
+ {
+ if ( cbFrame >= sizeof(VRDEVIDEOINPAYLOADHDR)
+ && cbFrame >= pFrame->u8HeaderLength)
+ {
+ uint32_t cbImage = cbFrame - pFrame->u8HeaderLength;
+ const uint8_t *pu8Image = cbImage > 0? (const uint8_t *)pFrame + pFrame->u8HeaderLength: NULL;
+
+ mpDrv->pIWebcamUp->pfnWebcamUpFrame(mpDrv->pIWebcamUp,
+ mpRemote->u64DeviceId,
+ (PDMIWEBCAM_FRAMEHDR *)pFrame,
+ pFrame->u8HeaderLength,
+ pu8Image,
+ cbImage);
+ }
+ }
+}
+
+int EmWebcam::SendControl(EMWEBCAMDRV *pDrv, void *pvUser, uint64_t u64DeviceId,
+ const VRDEVIDEOINCTRLHDR *pControl, uint32_t cbControl)
+{
+ AssertReturn(pDrv == mpDrv, VERR_NOT_SUPPORTED);
+
+ int rc = VINF_SUCCESS;
+
+ EMWEBCAMREQCTX *pCtx = NULL;
+
+ /* Verify that there is a remote device. */
+ if ( !mpRemote
+ || mpRemote->u64DeviceId != u64DeviceId)
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pCtx = (EMWEBCAMREQCTX *)RTMemAlloc(sizeof(EMWEBCAMREQCTX));
+ if (!pCtx)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pCtx->pRemote = mpRemote;
+ pCtx->pvUser = pvUser;
+
+ rc = mParent->VideoInControl(pCtx, &mpRemote->deviceHandle, pControl, cbControl);
+
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pCtx);
+ }
+ }
+
+ return rc;
+}
+
+/* static */ DECLCALLBACK(void *) EmWebcam::drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PEMWEBCAMDRV pThis = PDMINS_2_DATA(pDrvIns, PEMWEBCAMDRV);
+
+ LogFlowFunc(("pszIID:%s\n", pszIID));
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIWEBCAMDOWN, &pThis->IWebcamDown);
+ return NULL;
+}
+
+/* static */ DECLCALLBACK(void) EmWebcam::drvDestruct(PPDMDRVINS pDrvIns)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ PEMWEBCAMDRV pThis = PDMINS_2_DATA(pDrvIns, PEMWEBCAMDRV);
+ EMWEBCAMREMOTE *pRemote = pThis->pRemote;
+
+ LogFlowFunc(("iInstance %d, pRemote %p, pIWebcamUp %p\n",
+ pDrvIns->iInstance, pRemote, pThis->pIWebcamUp));
+
+ if (pRemote && pRemote->pEmWebcam)
+ {
+ pRemote->pEmWebcam->EmWebcamDestruct(pThis);
+ }
+}
+
+/* static */ DECLCALLBACK(int) EmWebcam::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ LogFlowFunc(("iInstance:%d, pCfg:%p, fFlags:%x\n", pDrvIns->iInstance, pCfg, fFlags));
+
+ PEMWEBCAMDRV pThis = PDMINS_2_DATA(pDrvIns, PEMWEBCAMDRV);
+
+ AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
+ ("Configuration error: Not possible to attach anything to this driver!\n"),
+ VERR_PDM_DRVINS_NO_ATTACH);
+
+ /* Check early that there is a device. No need to init anything if there is no device. */
+ pThis->pIWebcamUp = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIWEBCAMUP);
+ if (pThis->pIWebcamUp == NULL)
+ {
+ LogRel(("USBWEBCAM: Emulated webcam device does not exist.\n"));
+ return VERR_PDM_MISSING_INTERFACE;
+ }
+
+ void *pv = NULL;
+ int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
+ if (!RT_VALID_PTR(pv))
+ rc = VERR_INVALID_PARAMETER;
+ AssertMsgReturn(RT_SUCCESS(rc),
+ ("Configuration error: No/bad \"Object\" %p value! rc=%Rrc\n", pv, rc), rc);
+
+ /* Everything ok. Initialize. */
+ pThis->pRemote = (EMWEBCAMREMOTE *)pv;
+ pThis->pRemote->pEmWebcam->EmWebcamConstruct(pThis);
+
+ pDrvIns->IBase.pfnQueryInterface = drvQueryInterface;
+
+ pThis->IWebcamDown.pfnWebcamDownReady = drvEmWebcamReady;
+ pThis->IWebcamDown.pfnWebcamDownControl = drvEmWebcamControl;
+
+ return VINF_SUCCESS;
+}
+
+/* static */ const PDMDRVREG EmWebcam::DrvReg =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName[32] */
+ "EmWebcam",
+ /* szRCMod[32] */
+ "",
+ /* szR0Mod[32] */
+ "",
+ /* pszDescription */
+ "Main Driver communicating with VRDE",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass */
+ PDM_DRVREG_CLASS_USB,
+ /* cMaxInstances */
+ 1,
+ /* cbInstance */
+ sizeof(EMWEBCAMDRV),
+ /* pfnConstruct */
+ EmWebcam::drvConstruct,
+ /* pfnDestruct */
+ EmWebcam::drvDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32VersionEnd */
+ PDM_DRVREG_VERSION
+};
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */
diff --git a/src/VBox/Main/src-client/VBoxDriversRegister.cpp b/src/VBox/Main/src-client/VBoxDriversRegister.cpp
index b98babe2..7fa0f998 100644
--- a/src/VBox/Main/src-client/VBoxDriversRegister.cpp
+++ b/src/VBox/Main/src-client/VBoxDriversRegister.cpp
@@ -25,9 +25,7 @@
#include "VMMDev.h"
#include "AudioSnifferInterface.h"
#include "Nvram.h"
-#ifdef VBOX_WITH_USB_VIDEO
-# include "UsbWebcamInterface.h"
-#endif
+#include "UsbWebcamInterface.h"
#ifdef VBOX_WITH_USB_CARDREADER
# include "UsbCardReader.h"
#endif
@@ -77,11 +75,9 @@ extern "C" DECLEXPORT(int) VBoxDriversRegister(PCPDMDRVREGCB pCallbacks, uint32_
if (RT_FAILURE(rc))
return rc;
-#ifdef VBOX_WITH_USB_VIDEO
- rc = pCallbacks->pfnRegister(pCallbacks, &UsbWebcamInterface::DrvReg);
+ rc = pCallbacks->pfnRegister(pCallbacks, &EmWebcam::DrvReg);
if (RT_FAILURE(rc))
return rc;
-#endif
#ifdef VBOX_WITH_USB_CARDREADER
rc = pCallbacks->pfnRegister(pCallbacks, &UsbCardReader::DrvReg);
diff --git a/src/VBox/Main/src-client/VMMDevInterface.cpp b/src/VBox/Main/src-client/VMMDevInterface.cpp
index 5c3809fe..b4f4da47 100644
--- a/src/VBox/Main/src-client/VMMDevInterface.cpp
+++ b/src/VBox/Main/src-client/VMMDevInterface.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -178,6 +178,30 @@ DECLCALLBACK(void) vmmdevUpdateGuestStatus(PPDMIVMMDEVCONNECTOR pInterface, uint
/**
+ * @interface_method_impl{PDMIVMMDEVCONNECTOR,pfnUpdateGuestUserState}
+ */
+DECLCALLBACK(void) vmmdevUpdateGuestUserState(PPDMIVMMDEVCONNECTOR pInterface,
+ const char *pszUser, const char *pszDomain,
+ uint32_t uState,
+ const uint8_t *puDetails, uint32_t cbDetails)
+{
+ PDRVMAINVMMDEV pDrv = PDMIVMMDEVCONNECTOR_2_MAINVMMDEV(pInterface);
+ AssertPtr(pDrv);
+ Console *pConsole = pDrv->pVMMDev->getParent();
+ AssertPtr(pConsole);
+
+ /* Store that information in IGuest. */
+ Guest* pGuest = pConsole->getGuest();
+ AssertPtr(pGuest);
+ if (!pGuest)
+ return;
+
+ pGuest->onUserStateChange(Bstr(pszUser), Bstr(pszDomain), (VBoxGuestUserState)uState,
+ puDetails, cbDetails);
+}
+
+
+/**
* Reports Guest Additions API and OS version.
*
* Called whenever the Additions issue a guest version report request or the VM
@@ -745,44 +769,39 @@ DECLCALLBACK(void *) VMMDev::drvQueryInterface(PPDMIBASE pInterface, const char
}
/**
- * Destruct a VMMDev driver instance.
- *
- * @returns VBox status.
- * @param pDrvIns The driver instance data.
+ * @interface_method_impl{PDMDRVREG,pfnReset}
*/
-DECLCALLBACK(void) VMMDev::drvDestruct(PPDMDRVINS pDrvIns)
+DECLCALLBACK(void) VMMDev::drvReset(PPDMDRVINS pDrvIns)
{
- PDRVMAINVMMDEV pData = PDMINS_2_DATA(pDrvIns, PDRVMAINVMMDEV);
- LogFlow(("VMMDev::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
+ LogFlow(("VMMDev::drvReset: iInstance=%d\n", pDrvIns->iInstance));
#ifdef VBOX_WITH_HGCM
- /* HGCM is shut down on the VMMDev destructor. */
+ HGCMHostReset ();
#endif /* VBOX_WITH_HGCM */
- if (pData->pVMMDev)
- pData->pVMMDev->mpDrv = NULL;
}
/**
- * Reset notification.
- *
- * @returns VBox status.
- * @param pDrvIns The driver instance data.
+ * @interface_method_impl{PDMDRVREG,pfnDestruct}
*/
-DECLCALLBACK(void) VMMDev::drvReset(PPDMDRVINS pDrvIns)
+DECLCALLBACK(void) VMMDev::drvDestruct(PPDMDRVINS pDrvIns)
{
- LogFlow(("VMMDev::drvReset: iInstance=%d\n", pDrvIns->iInstance));
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ PDRVMAINVMMDEV pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINVMMDEV);
+ LogFlow(("VMMDev::drvDestruct: iInstance=%d\n", pDrvIns->iInstance));
+
#ifdef VBOX_WITH_HGCM
- HGCMHostReset ();
+ /* HGCM is shut down on the VMMDev destructor. */
#endif /* VBOX_WITH_HGCM */
+ if (pThis->pVMMDev)
+ pThis->pVMMDev->mpDrv = NULL;
}
/**
- * Construct a VMMDev driver instance.
- *
- * @copydoc FNPDMDRVCONSTRUCT
+ * @interface_method_impl{PDMDRVREG,pfnConstruct}
*/
DECLCALLBACK(int) VMMDev::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
{
- PDRVMAINVMMDEV pData = PDMINS_2_DATA(pDrvIns, PDRVMAINVMMDEV);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVMAINVMMDEV pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINVMMDEV);
LogFlow(("Keyboard::drvConstruct: iInstance=%d\n", pDrvIns->iInstance));
/*
@@ -799,39 +818,40 @@ DECLCALLBACK(int) VMMDev::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle,
*/
pDrvIns->IBase.pfnQueryInterface = VMMDev::drvQueryInterface;
- pData->Connector.pfnUpdateGuestStatus = vmmdevUpdateGuestStatus;
- pData->Connector.pfnUpdateGuestInfo = vmmdevUpdateGuestInfo;
- pData->Connector.pfnUpdateGuestInfo2 = vmmdevUpdateGuestInfo2;
- pData->Connector.pfnUpdateGuestCapabilities = vmmdevUpdateGuestCapabilities;
- pData->Connector.pfnUpdateMouseCapabilities = vmmdevUpdateMouseCapabilities;
- pData->Connector.pfnUpdatePointerShape = vmmdevUpdatePointerShape;
- pData->Connector.pfnVideoAccelEnable = iface_VideoAccelEnable;
- pData->Connector.pfnVideoAccelFlush = iface_VideoAccelFlush;
- pData->Connector.pfnVideoModeSupported = vmmdevVideoModeSupported;
- pData->Connector.pfnGetHeightReduction = vmmdevGetHeightReduction;
- pData->Connector.pfnSetCredentialsJudgementResult = vmmdevSetCredentialsJudgementResult;
- pData->Connector.pfnSetVisibleRegion = vmmdevSetVisibleRegion;
- pData->Connector.pfnQueryVisibleRegion = vmmdevQueryVisibleRegion;
- pData->Connector.pfnReportStatistics = vmmdevReportStatistics;
- pData->Connector.pfnQueryStatisticsInterval = vmmdevQueryStatisticsInterval;
- pData->Connector.pfnQueryBalloonSize = vmmdevQueryBalloonSize;
- pData->Connector.pfnIsPageFusionEnabled = vmmdevIsPageFusionEnabled;
+ pThis->Connector.pfnUpdateGuestStatus = vmmdevUpdateGuestStatus;
+ pThis->Connector.pfnUpdateGuestUserState = vmmdevUpdateGuestUserState;
+ pThis->Connector.pfnUpdateGuestInfo = vmmdevUpdateGuestInfo;
+ pThis->Connector.pfnUpdateGuestInfo2 = vmmdevUpdateGuestInfo2;
+ pThis->Connector.pfnUpdateGuestCapabilities = vmmdevUpdateGuestCapabilities;
+ pThis->Connector.pfnUpdateMouseCapabilities = vmmdevUpdateMouseCapabilities;
+ pThis->Connector.pfnUpdatePointerShape = vmmdevUpdatePointerShape;
+ pThis->Connector.pfnVideoAccelEnable = iface_VideoAccelEnable;
+ pThis->Connector.pfnVideoAccelFlush = iface_VideoAccelFlush;
+ pThis->Connector.pfnVideoModeSupported = vmmdevVideoModeSupported;
+ pThis->Connector.pfnGetHeightReduction = vmmdevGetHeightReduction;
+ pThis->Connector.pfnSetCredentialsJudgementResult = vmmdevSetCredentialsJudgementResult;
+ pThis->Connector.pfnSetVisibleRegion = vmmdevSetVisibleRegion;
+ pThis->Connector.pfnQueryVisibleRegion = vmmdevQueryVisibleRegion;
+ pThis->Connector.pfnReportStatistics = vmmdevReportStatistics;
+ pThis->Connector.pfnQueryStatisticsInterval = vmmdevQueryStatisticsInterval;
+ pThis->Connector.pfnQueryBalloonSize = vmmdevQueryBalloonSize;
+ pThis->Connector.pfnIsPageFusionEnabled = vmmdevIsPageFusionEnabled;
#ifdef VBOX_WITH_HGCM
- pData->HGCMConnector.pfnConnect = iface_hgcmConnect;
- pData->HGCMConnector.pfnDisconnect = iface_hgcmDisconnect;
- pData->HGCMConnector.pfnCall = iface_hgcmCall;
+ pThis->HGCMConnector.pfnConnect = iface_hgcmConnect;
+ pThis->HGCMConnector.pfnDisconnect = iface_hgcmDisconnect;
+ pThis->HGCMConnector.pfnCall = iface_hgcmCall;
#endif
/*
* Get the IVMMDevPort interface of the above driver/device.
*/
- pData->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIVMMDEVPORT);
- AssertMsgReturn(pData->pUpPort, ("Configuration error: No VMMDev port interface above!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
+ pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIVMMDEVPORT);
+ AssertMsgReturn(pThis->pUpPort, ("Configuration error: No VMMDev port interface above!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
#ifdef VBOX_WITH_HGCM
- pData->pHGCMPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHGCMPORT);
- AssertMsgReturn(pData->pHGCMPort, ("Configuration error: No HGCM port interface above!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
+ pThis->pHGCMPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHGCMPORT);
+ AssertMsgReturn(pThis->pHGCMPort, ("Configuration error: No HGCM port interface above!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
#endif
/*
@@ -845,13 +865,13 @@ DECLCALLBACK(int) VMMDev::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle,
return rc;
}
- pData->pVMMDev = (VMMDev*)pv; /** @todo Check this cast! */
- pData->pVMMDev->mpDrv = pData;
+ pThis->pVMMDev = (VMMDev*)pv; /** @todo Check this cast! */
+ pThis->pVMMDev->mpDrv = pThis;
#ifdef VBOX_WITH_HGCM
- rc = pData->pVMMDev->hgcmLoadService(VBOXSHAREDFOLDERS_DLL,
+ rc = pThis->pVMMDev->hgcmLoadService(VBOXSHAREDFOLDERS_DLL,
"VBoxSharedFolders");
- pData->pVMMDev->fSharedFolderActive = RT_SUCCESS(rc);
+ pThis->pVMMDev->fSharedFolderActive = RT_SUCCESS(rc);
if (RT_SUCCESS(rc))
{
PPDMLED pLed;
diff --git a/src/VBox/Main/src-client/VideoRec.cpp b/src/VBox/Main/src-client/VideoRec.cpp
new file mode 100644
index 00000000..f47527e4
--- /dev/null
+++ b/src/VBox/Main/src-client/VideoRec.cpp
@@ -0,0 +1,885 @@
+/* $Id: VideoRec.cpp $ */
+/** @file
+ * Encodes the screen content in VPX format.
+ */
+
+/*
+ * 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;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN
+#include <VBox/log.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+
+#include <VBox/com/VirtualBox.h>
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+
+#include "EbmlWriter.h"
+#include "VideoRec.h"
+
+#define VPX_CODEC_DISABLE_COMPAT 1
+#include <vpx/vp8cx.h>
+#include <vpx/vpx_image.h>
+
+/** Default VPX codec to use */
+#define DEFAULTCODEC (vpx_codec_vp8_cx())
+
+static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStrm);
+static int videoRecRGBToYUV(PVIDEORECSTREAM pStrm);
+
+/* state to synchronized between threads */
+enum
+{
+ VIDREC_UNINITIALIZED = 0,
+ /* initialized, idle */
+ VIDREC_IDLE = 1,
+ /* currently in VideoRecCopyToIntBuf(), delay termination */
+ VIDREC_COPYING = 2,
+ /* signal that we are terminating */
+ VIDREC_TERMINATING = 3
+};
+
+/* Must be always accessible and therefore cannot be part of VIDEORECCONTEXT */
+static uint32_t g_enmState = VIDREC_UNINITIALIZED;
+
+
+typedef struct VIDEORECSTREAM
+{
+ /* container context */
+ EbmlGlobal Ebml;
+ /* VPX codec context */
+ vpx_codec_ctx_t VpxCodec;
+ /* VPX configuration */
+ vpx_codec_enc_cfg_t VpxConfig;
+ /* X resolution */
+ uint32_t uTargetWidth;
+ /* Y resolution */
+ uint32_t uTargetHeight;
+ /* X resolution of the last encoded picture */
+ uint32_t uLastSourceWidth;
+ /* Y resolution of the last encoded picture */
+ uint32_t uLastSourceHeight;
+ /* current frame number */
+ uint32_t cFrame;
+ /* RGB buffer containing the most recent frame of the framebuffer */
+ uint8_t *pu8RgbBuf;
+ /* YUV buffer the encode function fetches the frame from */
+ uint8_t *pu8YuvBuf;
+ /* VPX image context */
+ vpx_image_t VpxRawImage;
+ /* true if video recording is enabled */
+ bool fEnabled;
+ /* true if the RGB buffer is filled */
+ bool fRgbFilled;
+ /* pixel format of the current frame */
+ uint32_t u32PixelFormat;
+ /* minimal delay between two frames */
+ uint32_t uDelay;
+ /* time stamp of the last frame we encoded */
+ uint64_t u64LastTimeStamp;
+ /* time stamp of the current frame */
+ uint64_t u64TimeStamp;
+} VIDEORECSTREAM;
+
+typedef struct VIDEORECCONTEXT
+{
+ /* semaphore to signal the encoding worker thread */
+ RTSEMEVENT WaitEvent;
+ /* semaphore required during termination */
+ RTSEMEVENT TermEvent;
+ /* true if video recording is enabled */
+ bool fEnabled;
+ /* worker thread */
+ RTTHREAD Thread;
+ /* number of stream contexts */
+ uint32_t cScreens;
+ /* video recording stream contexts */
+ VIDEORECSTREAM Strm[1];
+} VIDEORECCONTEXT;
+
+
+/**
+ * Iterator class for running through a BGRA32 image buffer and converting
+ * it to RGB.
+ */
+class ColorConvBGRA32Iter
+{
+private:
+ enum { PIX_SIZE = 4 };
+public:
+ ColorConvBGRA32Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuf)
+ {
+ LogFlow(("width = %d height=%d aBuf=%lx\n", aWidth, aHeight, aBuf));
+ mPos = 0;
+ mSize = aWidth * aHeight * PIX_SIZE;
+ mBuf = aBuf;
+ }
+ /**
+ * Convert the next pixel to RGB.
+ * @returns true on success, false if we have reached the end of the buffer
+ * @param aRed where to store the red value
+ * @param aGreen where to store the green value
+ * @param aBlue where to store the blue value
+ */
+ bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
+ {
+ bool rc = false;
+ if (mPos + PIX_SIZE <= mSize)
+ {
+ *aRed = mBuf[mPos + 2];
+ *aGreen = mBuf[mPos + 1];
+ *aBlue = mBuf[mPos ];
+ mPos += PIX_SIZE;
+ rc = true;
+ }
+ return rc;
+ }
+
+ /**
+ * Skip forward by a certain number of pixels
+ * @param aPixels how many pixels to skip
+ */
+ void skip(unsigned aPixels)
+ {
+ mPos += PIX_SIZE * aPixels;
+ }
+private:
+ /** Size of the picture buffer */
+ unsigned mSize;
+ /** Current position in the picture buffer */
+ unsigned mPos;
+ /** Address of the picture buffer */
+ uint8_t *mBuf;
+};
+
+/**
+ * Iterator class for running through an BGR24 image buffer and converting
+ * it to RGB.
+ */
+class ColorConvBGR24Iter
+{
+private:
+ enum { PIX_SIZE = 3 };
+public:
+ ColorConvBGR24Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuf)
+ {
+ mPos = 0;
+ mSize = aWidth * aHeight * PIX_SIZE;
+ mBuf = aBuf;
+ }
+ /**
+ * Convert the next pixel to RGB.
+ * @returns true on success, false if we have reached the end of the buffer
+ * @param aRed where to store the red value
+ * @param aGreen where to store the green value
+ * @param aBlue where to store the blue value
+ */
+ bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
+ {
+ bool rc = false;
+ if (mPos + PIX_SIZE <= mSize)
+ {
+ *aRed = mBuf[mPos + 2];
+ *aGreen = mBuf[mPos + 1];
+ *aBlue = mBuf[mPos ];
+ mPos += PIX_SIZE;
+ rc = true;
+ }
+ return rc;
+ }
+
+ /**
+ * Skip forward by a certain number of pixels
+ * @param aPixels how many pixels to skip
+ */
+ void skip(unsigned aPixels)
+ {
+ mPos += PIX_SIZE * aPixels;
+ }
+private:
+ /** Size of the picture buffer */
+ unsigned mSize;
+ /** Current position in the picture buffer */
+ unsigned mPos;
+ /** Address of the picture buffer */
+ uint8_t *mBuf;
+};
+
+/**
+ * Iterator class for running through an BGR565 image buffer and converting
+ * it to RGB.
+ */
+class ColorConvBGR565Iter
+{
+private:
+ enum { PIX_SIZE = 2 };
+public:
+ ColorConvBGR565Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuf)
+ {
+ mPos = 0;
+ mSize = aWidth * aHeight * PIX_SIZE;
+ mBuf = aBuf;
+ }
+ /**
+ * Convert the next pixel to RGB.
+ * @returns true on success, false if we have reached the end of the buffer
+ * @param aRed where to store the red value
+ * @param aGreen where to store the green value
+ * @param aBlue where to store the blue value
+ */
+ bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
+ {
+ bool rc = false;
+ if (mPos + PIX_SIZE <= mSize)
+ {
+ unsigned uFull = (((unsigned) mBuf[mPos + 1]) << 8)
+ | ((unsigned) mBuf[mPos]);
+ *aRed = (uFull >> 8) & ~7;
+ *aGreen = (uFull >> 3) & ~3 & 0xff;
+ *aBlue = (uFull << 3) & ~7 & 0xff;
+ mPos += PIX_SIZE;
+ rc = true;
+ }
+ return rc;
+ }
+
+ /**
+ * Skip forward by a certain number of pixels
+ * @param aPixels how many pixels to skip
+ */
+ void skip(unsigned aPixels)
+ {
+ mPos += PIX_SIZE * aPixels;
+ }
+private:
+ /** Size of the picture buffer */
+ unsigned mSize;
+ /** Current position in the picture buffer */
+ unsigned mPos;
+ /** Address of the picture buffer */
+ uint8_t *mBuf;
+};
+
+/**
+ * Convert an image to YUV420p format
+ * @returns true on success, false on failure
+ * @param aWidth width of image
+ * @param aHeight height of image
+ * @param aDestBuf an allocated memory buffer large enough to hold the
+ * destination image (i.e. width * height * 12bits)
+ * @param aSrcBuf the source image as an array of bytes
+ */
+template <class T>
+inline bool colorConvWriteYUV420p(unsigned aWidth, unsigned aHeight,
+ uint8_t *aDestBuf, uint8_t *aSrcBuf)
+{
+ AssertReturn(0 == (aWidth & 1), false);
+ AssertReturn(0 == (aHeight & 1), false);
+ bool rc = true;
+ T iter1(aWidth, aHeight, aSrcBuf);
+ T iter2 = iter1;
+ iter2.skip(aWidth);
+ unsigned cPixels = aWidth * aHeight;
+ unsigned offY = 0;
+ unsigned offU = cPixels;
+ unsigned offV = cPixels + cPixels / 4;
+ for (unsigned i = 0; (i < aHeight / 2) && rc; ++i)
+ {
+ for (unsigned j = 0; (j < aWidth / 2) && rc; ++j)
+ {
+ unsigned red, green, blue, u, v;
+ rc = iter1.getRGB(&red, &green, &blue);
+ if (rc)
+ {
+ aDestBuf[offY] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
+ u = (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
+ v = (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
+ rc = iter1.getRGB(&red, &green, &blue);
+ }
+ if (rc)
+ {
+ aDestBuf[offY + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
+ u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
+ v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
+ rc = iter2.getRGB(&red, &green, &blue);
+ }
+ if (rc)
+ {
+ aDestBuf[offY + aWidth] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
+ u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
+ v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
+ rc = iter2.getRGB(&red, &green, &blue);
+ }
+ if (rc)
+ {
+ aDestBuf[offY + aWidth + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
+ u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
+ v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
+ aDestBuf[offU] = u;
+ aDestBuf[offV] = v;
+ offY += 2;
+ ++offU;
+ ++offV;
+ }
+ }
+ if (rc)
+ {
+ iter1.skip(aWidth);
+ iter2.skip(aWidth);
+ offY += aWidth;
+ }
+ }
+ return rc;
+}
+
+/**
+ * Convert an image to RGB24 format
+ * @returns true on success, false on failure
+ * @param aWidth width of image
+ * @param aHeight height of image
+ * @param aDestBuf an allocated memory buffer large enough to hold the
+ * destination image (i.e. width * height * 12bits)
+ * @param aSrcBuf the source image as an array of bytes
+ */
+template <class T>
+inline bool colorConvWriteRGB24(unsigned aWidth, unsigned aHeight,
+ uint8_t *aDestBuf, uint8_t *aSrcBuf)
+{
+ enum { PIX_SIZE = 3 };
+ bool rc = true;
+ AssertReturn(0 == (aWidth & 1), false);
+ AssertReturn(0 == (aHeight & 1), false);
+ T iter(aWidth, aHeight, aSrcBuf);
+ unsigned cPixels = aWidth * aHeight;
+ for (unsigned i = 0; i < cPixels && rc; ++i)
+ {
+ unsigned red, green, blue;
+ rc = iter.getRGB(&red, &green, &blue);
+ if (rc)
+ {
+ aDestBuf[i * PIX_SIZE ] = red;
+ aDestBuf[i * PIX_SIZE + 1] = green;
+ aDestBuf[i * PIX_SIZE + 2] = blue;
+ }
+ }
+ return rc;
+}
+
+/**
+ * Worker thread for all streams.
+ *
+ * RGB/YUV conversion and encoding.
+ */
+static DECLCALLBACK(int) videoRecThread(RTTHREAD Thread, void *pvUser)
+{
+ PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)pvUser;
+ for (;;)
+ {
+ int rc = RTSemEventWait(pCtx->WaitEvent, RT_INDEFINITE_WAIT);
+ AssertRCBreak(rc);
+
+ if (ASMAtomicReadU32(&g_enmState) == VIDREC_TERMINATING)
+ break;
+ for (unsigned uScreen = 0; uScreen < pCtx->cScreens; uScreen++)
+ {
+ PVIDEORECSTREAM pStrm = &pCtx->Strm[uScreen];
+ if ( pStrm->fEnabled
+ && ASMAtomicReadBool(&pStrm->fRgbFilled))
+ {
+ rc = videoRecRGBToYUV(pStrm);
+ ASMAtomicWriteBool(&pStrm->fRgbFilled, false);
+ if (RT_SUCCESS(rc))
+ rc = videoRecEncodeAndWrite(pStrm);
+ if (RT_FAILURE(rc))
+ {
+ static unsigned cErrors = 100;
+ if (cErrors > 0)
+ {
+ LogRel(("Error %Rrc encoding / writing video frame\n", rc));
+ cErrors--;
+ }
+ }
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * VideoRec utility function to create video recording context.
+ *
+ * @returns IPRT status code.
+ * @param ppCtx Video recording context
+ * @param cScreens Number of screens.
+ */
+int VideoRecContextCreate(PVIDEORECCONTEXT *ppCtx, uint32_t cScreens)
+{
+ Assert(ASMAtomicReadU32(&g_enmState) == VIDREC_UNINITIALIZED);
+
+ PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)RTMemAllocZ(RT_OFFSETOF(VIDEORECCONTEXT, Strm[cScreens]));
+ *ppCtx = pCtx;
+ AssertPtrReturn(pCtx, VERR_NO_MEMORY);
+
+ pCtx->cScreens = cScreens;
+ for (unsigned uScreen = 0; uScreen < cScreens; uScreen++)
+ pCtx->Strm[uScreen].Ebml.last_pts_ms = -1;
+
+ int rc = RTSemEventCreate(&pCtx->WaitEvent);
+ AssertRCReturn(rc, rc);
+
+ rc = RTSemEventCreate(&pCtx->TermEvent);
+ AssertRCReturn(rc, rc);
+
+ rc = RTThreadCreate(&pCtx->Thread, videoRecThread, (void*)pCtx, 0,
+ RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE, "VideoRec");
+ AssertRCReturn(rc, rc);
+
+ ASMAtomicWriteU32(&g_enmState, VIDREC_IDLE);
+ return VINF_SUCCESS;
+}
+
+/**
+ * VideoRec utility function to initialize video recording context.
+ *
+ * @returns IPRT status code.
+ * @param pCtx Pointer to video recording context to initialize Framebuffer width.
+ * @param uScreeen Screen number.
+ * @param strFile File to save the recorded data
+ * @param uTargetWidth Width of the target image in the video recoriding file (movie)
+ * @param uTargetHeight Height of the target image in video recording file.
+ */
+int VideoRecStrmInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen, const char *pszFile,
+ uint32_t uWidth, uint32_t uHeight, uint32_t uRate, uint32_t uFps)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_PARAMETER);
+ AssertReturn(uScreen < pCtx->cScreens, VERR_INVALID_PARAMETER);
+
+ PVIDEORECSTREAM pStrm = &pCtx->Strm[uScreen];
+ pStrm->uTargetWidth = uWidth;
+ pStrm->uTargetHeight = uHeight;
+ pStrm->pu8RgbBuf = (uint8_t *)RTMemAllocZ(uWidth * uHeight * 4);
+ AssertReturn(pStrm->pu8RgbBuf, VERR_NO_MEMORY);
+
+ /* Play safe: the file must not exist, overwriting is potentially
+ * hazardous as nothing prevents the user from picking a file name of some
+ * other important file, causing unintentional data loss. */
+ int rc = RTFileOpen(&pStrm->Ebml.file, pszFile,
+ RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("Failed to create the video capture output file \"%s\" (%Rrc)\n", pszFile, rc));
+ return rc;
+ }
+
+ vpx_codec_err_t rcv = vpx_codec_enc_config_default(DEFAULTCODEC, &pStrm->VpxConfig, 0);
+ if (rcv != VPX_CODEC_OK)
+ {
+ LogFlow(("Failed to configure codec\n", vpx_codec_err_to_string(rcv)));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* target bitrate in kilobits per second */
+ pStrm->VpxConfig.rc_target_bitrate = uRate;
+ /* frame width */
+ pStrm->VpxConfig.g_w = uWidth;
+ /* frame height */
+ pStrm->VpxConfig.g_h = uHeight;
+ /* 1ms per frame */
+ pStrm->VpxConfig.g_timebase.num = 1;
+ pStrm->VpxConfig.g_timebase.den = 1000;
+ /* disable multithreading */
+ pStrm->VpxConfig.g_threads = 0;
+
+ pStrm->uDelay = 1000 / uFps;
+
+ struct vpx_rational arg_framerate = { 30, 1 };
+ rc = Ebml_WriteWebMFileHeader(&pStrm->Ebml, &pStrm->VpxConfig, &arg_framerate);
+ AssertRCReturn(rc, rc);
+
+ /* Initialize codec */
+ rcv = vpx_codec_enc_init(&pStrm->VpxCodec, DEFAULTCODEC, &pStrm->VpxConfig, 0);
+ if (rcv != VPX_CODEC_OK)
+ {
+ LogFlow(("Failed to initialize VP8 encoder %s", vpx_codec_err_to_string(rcv)));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (!vpx_img_alloc(&pStrm->VpxRawImage, VPX_IMG_FMT_I420, uWidth, uHeight, 1))
+ {
+ LogFlow(("Failed to allocate image %dx%d", uWidth, uHeight));
+ return VERR_NO_MEMORY;
+ }
+ pStrm->pu8YuvBuf = pStrm->VpxRawImage.planes[0];
+
+ pCtx->fEnabled = true;
+ pStrm->fEnabled = true;
+ return VINF_SUCCESS;
+}
+
+/**
+ * VideoRec utility function to close the video recording context.
+ *
+ * @param pCtx Pointer to video recording context.
+ */
+void VideoRecContextClose(PVIDEORECCONTEXT pCtx)
+{
+ if (!pCtx)
+ return;
+
+ uint32_t enmState = VIDREC_IDLE;
+ for (;;)
+ {
+ if (ASMAtomicCmpXchgExU32(&g_enmState, VIDREC_TERMINATING, enmState, &enmState))
+ break;
+ if (enmState == VIDREC_UNINITIALIZED)
+ return;
+ }
+ if (enmState == VIDREC_COPYING)
+ {
+ int rc = RTSemEventWait(pCtx->TermEvent, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+ }
+
+ RTSemEventSignal(pCtx->WaitEvent);
+ RTThreadWait(pCtx->Thread, 10000, NULL);
+ RTSemEventDestroy(pCtx->WaitEvent);
+ RTSemEventDestroy(pCtx->TermEvent);
+
+ for (unsigned uScreen = 0; uScreen < pCtx->cScreens; uScreen++)
+ {
+ PVIDEORECSTREAM pStrm = &pCtx->Strm[uScreen];
+ if (pStrm->fEnabled)
+ {
+ if (pStrm->Ebml.file != NIL_RTFILE)
+ {
+ int rc = Ebml_WriteWebMFileFooter(&pStrm->Ebml, 0);
+ AssertRC(rc);
+ RTFileClose(pStrm->Ebml.file);
+ pStrm->Ebml.file = NIL_RTFILE;
+ }
+ if (pStrm->Ebml.cue_list)
+ {
+ RTMemFree(pStrm->Ebml.cue_list);
+ pStrm->Ebml.cue_list = NULL;
+ }
+ vpx_img_free(&pStrm->VpxRawImage);
+ vpx_codec_err_t rcv = vpx_codec_destroy(&pStrm->VpxCodec);
+ Assert(rcv == VPX_CODEC_OK);
+ RTMemFree(pStrm->pu8RgbBuf);
+ pStrm->pu8RgbBuf = NULL;
+ }
+ }
+
+ RTMemFree(pCtx);
+
+ ASMAtomicWriteU32(&g_enmState, VIDREC_UNINITIALIZED);
+}
+
+/**
+ * VideoRec utility function to check if recording is enabled.
+ *
+ * @returns true if recording is enabled
+ * @param pCtx Pointer to video recording context.
+ */
+bool VideoRecIsEnabled(PVIDEORECCONTEXT pCtx)
+{
+ uint32_t enmState = ASMAtomicReadU32(&g_enmState);
+ return ( enmState == VIDREC_IDLE
+ || enmState == VIDREC_COPYING);
+}
+
+/**
+ * VideoRec utility function to check if recording engine is ready to accept a new frame
+ * for the given screen.
+ *
+ * @returns true if recording engine is ready
+ * @param pCtx Pointer to video recording context.
+ * @param uScreen screen id.
+ * @param u64TimeStamp current time stamp
+ */
+bool VideoRecIsReady(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t u64TimeStamp)
+{
+ uint32_t enmState = ASMAtomicReadU32(&g_enmState);
+ if (enmState != VIDREC_IDLE)
+ return false;
+
+ PVIDEORECSTREAM pStrm = &pCtx->Strm[uScreen];
+ if (!pStrm->fEnabled)
+ return false;
+
+ if (u64TimeStamp < pStrm->u64LastTimeStamp + pStrm->uDelay)
+ return false;
+
+ if (ASMAtomicReadBool(&pStrm->fRgbFilled))
+ return false;
+
+ return true;
+}
+
+/**
+ * VideoRec utility function to encode the source image and write the encoded
+ * image to target file.
+ *
+ * @returns IPRT status code.
+ * @param pCtx Pointer to video recording context.
+ * @param uSourceWidth Width of the source image.
+ * @param uSourceHeight Height of the source image.
+ */
+static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStrm)
+{
+ /* presentation time stamp */
+ vpx_codec_pts_t pts = pStrm->u64TimeStamp;
+ vpx_codec_err_t rcv = vpx_codec_encode(&pStrm->VpxCodec,
+ &pStrm->VpxRawImage,
+ pts /* time stamp */,
+ 10 /* how long to show this frame */,
+ 0 /* flags */,
+ VPX_DL_REALTIME /* deadline */);
+ if (rcv != VPX_CODEC_OK)
+ {
+ LogFlow(("Failed to encode:%s\n", vpx_codec_err_to_string(rcv)));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ vpx_codec_iter_t iter = NULL;
+ int rc = VERR_NO_DATA;
+ for (;;)
+ {
+ const vpx_codec_cx_pkt_t *pkt = vpx_codec_get_cx_data(&pStrm->VpxCodec, &iter);
+ if (!pkt)
+ break;
+ switch (pkt->kind)
+ {
+ case VPX_CODEC_CX_FRAME_PKT:
+ rc = Ebml_WriteWebMBlock(&pStrm->Ebml, &pStrm->VpxConfig, pkt);
+ break;
+ default:
+ LogFlow(("Unexpected CODEC Packet.\n"));
+ break;
+ }
+ }
+
+ pStrm->cFrame++;
+ return rc;
+}
+
+/**
+ * VideoRec utility function to convert RGB to YUV.
+ *
+ * @returns IPRT status code.
+ * @param pCtx Pointer to video recording context.
+ */
+static int videoRecRGBToYUV(PVIDEORECSTREAM pStrm)
+{
+ switch (pStrm->u32PixelFormat)
+ {
+ case VPX_IMG_FMT_RGB32:
+ LogFlow(("32 bit\n"));
+ if (!colorConvWriteYUV420p<ColorConvBGRA32Iter>(pStrm->uTargetWidth,
+ pStrm->uTargetHeight,
+ pStrm->pu8YuvBuf,
+ pStrm->pu8RgbBuf))
+ return VERR_GENERAL_FAILURE;
+ break;
+ case VPX_IMG_FMT_RGB24:
+ LogFlow(("24 bit\n"));
+ if (!colorConvWriteYUV420p<ColorConvBGR24Iter>(pStrm->uTargetWidth,
+ pStrm->uTargetHeight,
+ pStrm->pu8YuvBuf,
+ pStrm->pu8RgbBuf))
+ return VERR_GENERAL_FAILURE;
+ break;
+ case VPX_IMG_FMT_RGB565:
+ LogFlow(("565 bit\n"));
+ if (!colorConvWriteYUV420p<ColorConvBGR565Iter>(pStrm->uTargetWidth,
+ pStrm->uTargetHeight,
+ pStrm->pu8YuvBuf,
+ pStrm->pu8RgbBuf))
+ return VERR_GENERAL_FAILURE;
+ break;
+ default:
+ return VERR_GENERAL_FAILURE;
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * VideoRec utility function to copy a source image (FrameBuf) to the intermediate
+ * RGB buffer. This function is executed only once per time.
+ *
+ * @thread EMT
+ *
+ * @returns IPRT status code.
+ * @param pCtx Pointer to the video recording context.
+ * @param uScreen Screen number.
+ * @param x Starting x coordinate of the source buffer (Framebuffer).
+ * @param y Starting y coordinate of the source buffer (Framebuffer).
+ * @param uPixelFormat Pixel Format.
+ * @param uBitsPerPixel Bits Per Pixel
+ * @param uBytesPerLine Bytes per source scanlineName.
+ * @param uSourceWidth Width of the source image (framebuffer).
+ * @param uSourceHeight Height of the source image (framebuffer).
+ * @param pu8BufAddr Pointer to source image(framebuffer).
+ * @param u64TimeStamp Time stamp (milliseconds).
+ */
+int VideoRecCopyToIntBuf(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint32_t x, uint32_t y,
+ uint32_t uPixelFormat, uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
+ uint32_t uSourceWidth, uint32_t uSourceHeight, uint8_t *pu8BufAddr,
+ uint64_t u64TimeStamp)
+{
+ /* Do not execute during termination and guard against termination */
+ if (!ASMAtomicCmpXchgU32(&g_enmState, VIDREC_COPYING, VIDREC_IDLE))
+ return VINF_TRY_AGAIN;
+
+ int rc = VINF_SUCCESS;
+ do
+ {
+ AssertPtrBreakStmt(pu8BufAddr, rc = VERR_INVALID_PARAMETER);
+ AssertBreakStmt(uSourceWidth, rc = VERR_INVALID_PARAMETER);
+ AssertBreakStmt(uSourceHeight, rc = VERR_INVALID_PARAMETER);
+ AssertBreakStmt(uScreen < pCtx->cScreens, rc = VERR_INVALID_PARAMETER);
+
+ PVIDEORECSTREAM pStrm = &pCtx->Strm[uScreen];
+ if (!pStrm->fEnabled)
+ {
+ rc = VINF_TRY_AGAIN; /* not (yet) enabled */
+ break;
+ }
+ if (u64TimeStamp < pStrm->u64LastTimeStamp + pStrm->uDelay)
+ {
+ rc = VINF_TRY_AGAIN; /* respect maximum frames per second */
+ break;
+ }
+ if (ASMAtomicReadBool(&pStrm->fRgbFilled))
+ {
+ rc = VERR_TRY_AGAIN; /* previous frame not yet encoded */
+ break;
+ }
+
+ pStrm->u64LastTimeStamp = u64TimeStamp;
+
+ int xDiff = ((int)pStrm->uTargetWidth - (int)uSourceWidth) / 2;
+ uint32_t w = uSourceWidth;
+ if ((int)w + xDiff + (int)x <= 0) /* nothing visible */
+ {
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ uint32_t destX;
+ if ((int)x < -xDiff)
+ {
+ w += xDiff + x;
+ x = -xDiff;
+ destX = 0;
+ }
+ else
+ destX = x + xDiff;
+
+ uint32_t h = uSourceHeight;
+ int yDiff = ((int)pStrm->uTargetHeight - (int)uSourceHeight) / 2;
+ if ((int)h + yDiff + (int)y <= 0) /* nothing visible */
+ {
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ uint32_t destY;
+ if ((int)y < -yDiff)
+ {
+ h += yDiff + (int)y;
+ y = -yDiff;
+ destY = 0;
+ }
+ else
+ destY = y + yDiff;
+
+ if ( destX > pStrm->uTargetWidth
+ || destY > pStrm->uTargetHeight)
+ {
+ rc = VERR_INVALID_PARAMETER; /* nothing visible */
+ break;
+ }
+
+ if (destX + w > pStrm->uTargetWidth)
+ w = pStrm->uTargetWidth - destX;
+
+ if (destY + h > pStrm->uTargetHeight)
+ h = pStrm->uTargetHeight - destY;
+
+ /* Calculate bytes per pixel */
+ uint32_t bpp = 1;
+ if (uPixelFormat == FramebufferPixelFormat_FOURCC_RGB)
+ {
+ switch (uBitsPerPixel)
+ {
+ case 32:
+ pStrm->u32PixelFormat = VPX_IMG_FMT_RGB32;
+ bpp = 4;
+ break;
+ case 24:
+ pStrm->u32PixelFormat = VPX_IMG_FMT_RGB24;
+ bpp = 3;
+ break;
+ case 16:
+ pStrm->u32PixelFormat = VPX_IMG_FMT_RGB565;
+ bpp = 2;
+ break;
+ default:
+ AssertMsgFailed(("Unknown color depth! mBitsPerPixel=%d\n", uBitsPerPixel));
+ break;
+ }
+ }
+ else
+ AssertMsgFailed(("Unknown pixel format! mPixelFormat=%d\n", uPixelFormat));
+
+ /* One of the dimensions of the current frame is smaller than before so
+ * clear the entire buffer to prevent artifacts from the previous frame */
+ if ( uSourceWidth < pStrm->uLastSourceWidth
+ || uSourceHeight < pStrm->uLastSourceHeight)
+ memset(pStrm->pu8RgbBuf, 0, pStrm->uTargetWidth * pStrm->uTargetHeight * 4);
+
+ pStrm->uLastSourceWidth = uSourceWidth;
+ pStrm->uLastSourceHeight = uSourceHeight;
+
+ /* Calculate start offset in source and destination buffers */
+ uint32_t offSrc = y * uBytesPerLine + x * bpp;
+ uint32_t offDst = (destY * pStrm->uTargetWidth + destX) * bpp;
+ /* do the copy */
+ for (unsigned int i = 0; i < h; i++)
+ {
+ /* Overflow check */
+ Assert(offSrc + w * bpp <= uSourceHeight * uBytesPerLine);
+ Assert(offDst + w * bpp <= pStrm->uTargetHeight * pStrm->uTargetWidth * bpp);
+ memcpy(pStrm->pu8RgbBuf + offDst, pu8BufAddr + offSrc, w * bpp);
+ offSrc += uBytesPerLine;
+ offDst += pStrm->uTargetWidth * bpp;
+ }
+
+ pStrm->u64TimeStamp = u64TimeStamp;
+
+ ASMAtomicWriteBool(&pStrm->fRgbFilled, true);
+ RTSemEventSignal(pCtx->WaitEvent);
+ } while (0);
+
+ if (!ASMAtomicCmpXchgU32(&g_enmState, VIDREC_IDLE, VIDREC_COPYING))
+ {
+ rc = RTSemEventSignal(pCtx->TermEvent);
+ AssertRC(rc);
+ }
+
+ return rc;
+}
diff --git a/src/VBox/Main/src-client/VideoRec.h b/src/VBox/Main/src-client/VideoRec.h
new file mode 100644
index 00000000..7d0d6fa3
--- /dev/null
+++ b/src/VBox/Main/src-client/VideoRec.h
@@ -0,0 +1,39 @@
+/* $Id: VideoRec.h $ */
+/** @file
+ * Encodes the screen content in VPX format.
+ */
+
+/*
+ * 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;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef ____H_VIDEOREC
+#define ____H_VIDEOREC
+
+struct VIDEORECCONTEXT;
+typedef struct VIDEORECCONTEXT *PVIDEORECCONTEXT;
+
+struct VIDEORECSTREAM;
+typedef struct VIDEORECSTREAM *PVIDEORECSTREAM;
+
+int VideoRecContextCreate(PVIDEORECCONTEXT *ppCtx, uint32_t cScreens);
+int VideoRecStrmInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen, const char *pszFile,
+ uint32_t uWidth, uint32_t uHeight, uint32_t uRate, uint32_t uFps);
+void VideoRecContextClose(PVIDEORECCONTEXT pCtx);
+bool VideoRecIsEnabled(PVIDEORECCONTEXT pCtx);
+int VideoRecCopyToIntBuf(PVIDEORECCONTEXT pCtx, uint32_t uScreen,
+ uint32_t x, uint32_t y, uint32_t uPixelFormat, uint32_t uBitsPerPixel,
+ uint32_t uBytesPerLine, uint32_t uGuestWidth, uint32_t uGuestHeight,
+ uint8_t *pu8BufferAddress, uint64_t u64TimeStamp);
+bool VideoRecIsReady(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t u64TimeStamp);
+
+#endif /* !____H_VIDEOREC */
+
diff --git a/src/VBox/Main/src-client/VirtualBoxClientImpl.cpp b/src/VBox/Main/src-client/VirtualBoxClientImpl.cpp
index 94ed652d..0e2405af 100644
--- a/src/VBox/Main/src-client/VirtualBoxClientImpl.cpp
+++ b/src/VBox/Main/src-client/VirtualBoxClientImpl.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2010 Oracle Corporation
+ * Copyright (C) 2010-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;
@@ -20,6 +20,7 @@
#include "AutoCaller.h"
#include "VBoxEvents.h"
#include "Logging.h"
+#include "VBox/com/ErrorInfo.h"
#include <iprt/asm.h>
#include <iprt/thread.h>
@@ -80,7 +81,7 @@ HRESULT VirtualBoxClient::init()
rc = unconst(mData.m_pEventSource).createObject();
AssertComRCReturnRC(rc);
- rc = mData.m_pEventSource->init(static_cast<IVirtualBoxClient *>(this));
+ rc = mData.m_pEventSource->init();
AssertComRCReturnRC(rc);
/* Setting up the VBoxSVC watcher thread. If anything goes wrong here it
@@ -200,6 +201,37 @@ STDMETHODIMP VirtualBoxClient::COMGETTER(EventSource)(IEventSource **aEventSourc
return mData.m_pEventSource.isNull() ? E_FAIL : S_OK;
}
+/**
+ * Checks a Machine object for any pending errors.
+ *
+ * @returns COM status code
+ * @param aMachine Machine object to check.
+ */
+STDMETHODIMP VirtualBoxClient::CheckMachineError(IMachine *aMachine)
+{
+ HRESULT rc;
+ CheckComArgNotNull(aMachine);
+
+ BOOL fAccessible = FALSE;
+ rc = aMachine->COMGETTER(Accessible)(&fAccessible);
+ if (FAILED(rc))
+ return setError(rc, tr("Could not check the accessibility status of the VM"));
+ else if (!fAccessible)
+ {
+ ComPtr<IVirtualBoxErrorInfo> pAccessError;
+ rc = aMachine->COMGETTER(AccessError)(pAccessError.asOutParam());
+ if (FAILED(rc))
+ return setError(rc, tr("Could not get the access error message of the VM"));
+ else
+ {
+ ErrorInfo info(pAccessError);
+ ErrorInfoKeeper eik(info);
+ return info.getResultCode();
+ }
+ }
+ return S_OK;
+}
+
// private methods
/////////////////////////////////////////////////////////////////////////////
diff --git a/src/VBox/Main/src-client/win/VBoxC.def b/src/VBox/Main/src-client/win/VBoxC.def
index 360d0f8e..250b9372 100644
--- a/src/VBox/Main/src-client/win/VBoxC.def
+++ b/src/VBox/Main/src-client/win/VBoxC.def
@@ -3,7 +3,7 @@
; VBoxC DLL Definition File.
;
-; Copyright (C) 2006-2007 Oracle Corporation
+; Copyright (C) 2006-2010 Oracle Corporation
;
; This file is part of VirtualBox Open Source Edition (OSE), as
; available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Main/src-client/win/VBoxClient-x86.def b/src/VBox/Main/src-client/win/VBoxClient-x86.def
new file mode 100644
index 00000000..c6894441
--- /dev/null
+++ b/src/VBox/Main/src-client/win/VBoxClient-x86.def
@@ -0,0 +1,26 @@
+; $Id: VBoxClient-x86.def $
+;; @file
+; VBoxClient-x86 DLL Definition File.
+;
+
+;
+; 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;
+; you can redistribute it and/or modify it under the terms of the GNU
+; General Public License (GPL) as published by the Free Software
+; Foundation, in version 2 as it comes in the "COPYING" file of the
+; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+;
+
+LIBRARY VBoxClient-x86.dll
+
+EXPORTS
+ ; COM entry points
+ DllGetClassObject PRIVATE
+ DllCanUnloadNow PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
+
diff --git a/src/VBox/Main/src-client/win/VBoxClient-x86.rc b/src/VBox/Main/src-client/win/VBoxClient-x86.rc
new file mode 100644
index 00000000..d5c4ae09
--- /dev/null
+++ b/src/VBox/Main/src-client/win/VBoxClient-x86.rc
@@ -0,0 +1,67 @@
+/* $Id: VBoxClient-x86.rc $ */
+/** @file
+ * VBoxC - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+#include "win/resource.h"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_VERSION_MAJOR_NR,VBOX_VERSION_MINOR_NR,VBOX_VERSION_BUILD_NR,0
+ PRODUCTVERSION VBOX_VERSION_MAJOR_NR,VBOX_VERSION_MINOR_NR,VBOX_VERSION_BUILD_NR,0
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG|VS_FF_PRIVATEBUILD|VS_FF_PRERELEASE
+#else
+ FILEFLAGS 0 // final version
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0 // not used
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual
+ BEGIN
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileDescription", "VirtualBox Interface (32-bit)\0"
+ VALUE "FileVersion", VBOX_VERSION_MAJOR "." VBOX_VERSION_MINOR "." VBOX_VERSION_BUILD "." VBOX_SVN_REV "\0"
+ VALUE "InternalName", "VBoxClient-x86.dll\0"
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "OriginalFilename","VBoxClient-x86.dll\0"
+ VALUE "ProductName", VBOX_PRODUCT "\0"
+ VALUE "ProductVersion", VBOX_VERSION_MAJOR "." VBOX_VERSION_MINOR "." VBOX_VERSION_BUILD ".r" VBOX_SVN_REV "\0"
+
+ VALUE "OLESelfRegister", ""
+
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// REGISTRY
+//
+
+IDR_VIRTUALBOX REGISTRY "VBoxClient-x86.rgs"
+
+1 TYPELIB "VirtualBox-x86.tlb"
+
diff --git a/src/VBox/Main/src-client/win/dllmain.cpp b/src/VBox/Main/src-client/win/dllmain.cpp
index db1181cb..2410e97c 100644
--- a/src/VBox/Main/src-client/win/dllmain.cpp
+++ b/src/VBox/Main/src-client/win/dllmain.cpp
@@ -1,11 +1,10 @@
+/* $Id: dllmain.cpp $ */
/** @file
- *
- * DLLMAIN - COM DLL exports
- *
+ * VBoxC - COM DLL exports and DLL init/term.
*/
/*
- * Copyright (C) 2006-2007 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;
@@ -16,6 +15,10 @@
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
#include "VBox/com/defs.h"
#include <SessionImpl.h>
@@ -25,7 +28,13 @@
#include <atlcom.h>
#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
CComModule _Module;
BEGIN_OBJECT_MAP(ObjectMap)
@@ -33,6 +42,46 @@ BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_VirtualBoxClient, VirtualBoxClient)
END_OBJECT_MAP()
+
+/** @def WITH_MANUAL_CLEANUP
+ * Manually clean up the registry. */
+#if defined(DEBUG) && !defined(VBOX_IN_32_ON_64_MAIN_API)
+//# define WITH_MANUAL_CLEANUP
+#endif
+
+
+#ifdef WITH_MANUAL_CLEANUP
+/** Type library GUIDs to clean up manually. */
+static const char * const g_apszTypelibGuids[] =
+{
+ "{46137EEC-703B-4FE5-AFD4-7C9BBBBA0259}",
+ "{d7569351-1750-46f0-936e-bd127d5bc264}",
+};
+
+/** Same as above but with a "Typelib\\" prefix. */
+static const char * const g_apszTypelibGuidKeys[] =
+{
+ "TypeLib\\{46137EEC-703B-4FE5-AFD4-7C9BBBBA0259}",
+ "TypeLib\\{d7569351-1750-46f0-936e-bd127d5bc264}",
+};
+
+/** Type library version to clean up manually. */
+static const char * const g_apszTypelibVersions[] =
+{
+ "1.0",
+ "1.3",
+};
+#endif
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+#ifdef WITH_MANUAL_CLEANUP
+static void removeOldMess(void);
+#endif
+
+
/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point
@@ -45,7 +94,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
DisableThreadLibraryCalls(hInstance);
// idempotent, so doesn't harm, and needed for COM embedding scenario
- RTR3InitDll(0);
+ RTR3InitDll(RTR3INIT_FLAGS_UNOBTRUSIVE);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
@@ -84,5 +133,271 @@ STDAPI DllRegisterServer(void)
STDAPI DllUnregisterServer(void)
{
- return _Module.UnregisterServer(TRUE);
+ HRESULT hrc = _Module.UnregisterServer(TRUE);
+#ifdef WITH_MANUAL_CLEANUP
+ removeOldMess();
+#endif
+ return hrc;
}
+
+#ifdef WITH_MANUAL_CLEANUP
+
+/**
+ * Checks if the typelib GUID is one of the ones we wish to clean up.
+ *
+ * @returns true if it should be cleaned up, false if not.
+ * @param pszTypelibGuid The typelib GUID as bracketed string.
+ */
+static bool isTypelibGuidToRemove(const char *pszTypelibGuid)
+{
+ unsigned i = RT_ELEMENTS(g_apszTypelibGuids);
+ while (i-- > 0)
+ if (!stricmp(g_apszTypelibGuids[i], pszTypelibGuid))
+ return true;
+ return false;
+}
+
+
+/**
+ * Checks if the typelib version is one of the ones we wish to clean up.
+ *
+ * @returns true if it should be cleaned up, false if not.
+ * @param pszTypelibVer The typelib version as string.
+ */
+static bool isTypelibVersionToRemove(const char *pszTypelibVer)
+{
+ unsigned i = RT_ELEMENTS(g_apszTypelibVersions);
+ while (i-- > 0)
+ if (!strcmp(g_apszTypelibVersions[i], pszTypelibVer))
+ return true;
+ return false;
+}
+
+
+/**
+ * Hack to clean out the class IDs belonging to obsolete typelibs on development
+ * boxes and such likes.
+ */
+static void removeOldClassIDs(HKEY hkeyClassesRoot)
+{
+ HKEY hkeyClsId;
+ LONG rc = RegOpenKeyExA(hkeyClassesRoot, "CLSID", NULL, DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
+ &hkeyClsId);
+ if (rc == ERROR_SUCCESS)
+ {
+ for (DWORD idxKey = 0;; idxKey++)
+ {
+ char szCurNm[128 + 128];
+ DWORD cbCurNm = 128;
+ rc = RegEnumKeyExA(hkeyClsId, idxKey, szCurNm, &cbCurNm, NULL, NULL, NULL, NULL);
+ if (rc == ERROR_NO_MORE_ITEMS)
+ break;
+
+ /*
+ * Get the typelib GUID and program ID with the class ID.
+ */
+ AssertBreak(rc == ERROR_SUCCESS);
+ strcpy(&szCurNm[cbCurNm], "\\TypeLib");
+ HKEY hkeyIfTypelib;
+ rc = RegOpenKeyExA(hkeyClsId, szCurNm, NULL, KEY_QUERY_VALUE, &hkeyIfTypelib);
+ if (rc != ERROR_SUCCESS)
+ continue;
+
+ char szTypelibGuid[128];
+ DWORD cbValue = sizeof(szTypelibGuid) - 1;
+ rc = RegQueryValueExA(hkeyIfTypelib, NULL, NULL, NULL, (PBYTE)&szTypelibGuid[0], &cbValue);
+ if (rc != ERROR_SUCCESS)
+ cbValue = 0;
+ szTypelibGuid[cbValue] = '\0';
+ RegCloseKey(hkeyIfTypelib);
+ if (!isTypelibGuidToRemove(szTypelibGuid))
+ continue;
+
+ /* ProgId */
+ strcpy(&szCurNm[cbCurNm], "\\ProgId");
+ HKEY hkeyIfProgId;
+ rc = RegOpenKeyExA(hkeyClsId, szCurNm, NULL, KEY_QUERY_VALUE, &hkeyIfProgId);
+ if (rc != ERROR_SUCCESS)
+ continue;
+
+ char szProgId[64];
+ cbValue = sizeof(szProgId) - 1;
+ rc = RegQueryValueExA(hkeyIfProgId, NULL, NULL, NULL, (PBYTE)&szProgId[0], &cbValue);
+ if (rc != ERROR_SUCCESS)
+ cbValue = 0;
+ szProgId[cbValue] = '\0';
+ RegCloseKey(hkeyIfProgId);
+ if (strnicmp(szProgId, RT_STR_TUPLE("VirtualBox.")))
+ continue;
+
+ /*
+ * Ok, it's an orphaned VirtualBox interface. Delete it.
+ */
+ szCurNm[cbCurNm] = '\0';
+ RTAssertMsg2("Should delete HCR/CLSID/%s\n", szCurNm);
+ //rc = SHDeleteKeyA(hkeyClsId, szCurNm);
+ Assert(rc == ERROR_SUCCESS);
+ }
+
+ RegCloseKey(hkeyClsId);
+ }
+}
+
+
+/**
+ * Hack to clean out the interfaces belonging to obsolete typelibs on
+ * development boxes and such likes.
+ */
+static void removeOldInterfaces(HKEY hkeyClassesRoot)
+{
+ HKEY hkeyInterface;
+ LONG rc = RegOpenKeyExA(hkeyClassesRoot, "Interface", NULL, DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
+ &hkeyInterface);
+ if (rc == ERROR_SUCCESS)
+ {
+ for (DWORD idxKey = 0;; idxKey++)
+ {
+ char szCurNm[128 + 128];
+ DWORD cbCurNm = 128;
+ rc = RegEnumKeyExA(hkeyInterface, idxKey, szCurNm, &cbCurNm, NULL, NULL, NULL, NULL);
+ if (rc == ERROR_NO_MORE_ITEMS)
+ break;
+
+ /*
+ * Get the typelib GUID and version associated with the interface.
+ */
+ AssertBreak(rc == ERROR_SUCCESS);
+ strcpy(&szCurNm[cbCurNm], "\\TypeLib");
+ HKEY hkeyIfTypelib;
+ rc = RegOpenKeyExA(hkeyInterface, szCurNm, NULL, KEY_QUERY_VALUE, &hkeyIfTypelib);
+ if (rc != ERROR_SUCCESS)
+ continue;
+
+ char szTypelibGuid[128];
+ DWORD cbValue = sizeof(szTypelibGuid) - 1;
+ rc = RegQueryValueExA(hkeyIfTypelib, NULL, NULL, NULL, (PBYTE)&szTypelibGuid[0], &cbValue);
+ if (rc != ERROR_SUCCESS)
+ cbValue = 0;
+ szTypelibGuid[cbValue] = '\0';
+ if (!isTypelibGuidToRemove(szTypelibGuid))
+ {
+ RegCloseKey(hkeyIfTypelib);
+ continue;
+ }
+
+ char szTypelibVer[64];
+ cbValue = sizeof(szTypelibVer) - 1;
+ rc = RegQueryValueExA(hkeyIfTypelib, "Version", NULL, NULL, (PBYTE)&szTypelibVer[0], &cbValue);
+ if (rc != ERROR_SUCCESS)
+ cbValue = 0;
+ szTypelibVer[cbValue] = '\0';
+
+ RegCloseKey(hkeyIfTypelib);
+
+ if (!isTypelibVersionToRemove(szTypelibVer))
+ continue;
+
+
+ /*
+ * Ok, it's an orphaned VirtualBox interface. Delete it.
+ */
+ szCurNm[cbCurNm] = '\0';
+ //RTAssertMsg2("Should delete HCR/Interface/%s\n", szCurNm);
+ rc = SHDeleteKeyA(hkeyInterface, szCurNm);
+ Assert(rc == ERROR_SUCCESS);
+ }
+
+ RegCloseKey(hkeyInterface);
+ }
+}
+
+
+/**
+ * Hack to clean obsolete typelibs on development boxes and such.
+ */
+static void removeOldTypelib(HKEY hkeyClassesRoot)
+{
+ /*
+ * Open it and verify the identity.
+ */
+ unsigned i = RT_ELEMENTS(g_apszTypelibGuidKeys);
+ while (i-- > 0)
+ {
+ HKEY hkeyTyplib;
+ LONG rc = RegOpenKeyExA(hkeyClassesRoot, g_apszTypelibGuidKeys[i], NULL,
+ DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &hkeyTyplib);
+ if (rc == ERROR_SUCCESS)
+ {
+ unsigned iVer = RT_ELEMENTS(g_apszTypelibVersions);
+ while (iVer-- > 0)
+ {
+ HKEY hkeyVer;
+ rc = RegOpenKeyExA(hkeyTyplib, g_apszTypelibVersions[iVer], NULL, KEY_READ, &hkeyVer);
+ if (rc == ERROR_SUCCESS)
+ {
+ char szValue[128];
+ DWORD cbValue = sizeof(szValue) - 1;
+ rc = RegQueryValueExA(hkeyVer, NULL, NULL, NULL, (PBYTE)&szValue[0], &cbValue);
+ if (rc == ERROR_SUCCESS)
+ {
+ szValue[cbValue] = '\0';
+ if (!strcmp(szValue, "VirtualBox Type Library"))
+ {
+ RegCloseKey(hkeyVer);
+ hkeyVer = NULL;
+
+ /*
+ * Delete the type library.
+ */
+ //RTAssertMsg2("Should delete HCR\\%s\\%s\n", g_apszTypelibGuidKeys[i], g_apszTypelibVersions[iVer]);
+ rc = SHDeleteKeyA(hkeyTyplib, g_apszTypelibVersions[iVer]);
+ Assert(rc == ERROR_SUCCESS);
+ }
+ }
+
+ if (hkeyVer != NULL)
+ RegCloseKey(hkeyVer);
+ }
+ }
+ RegCloseKey(hkeyTyplib);
+
+ /*
+ * The typelib key should be empty now, so we can try remove it (non-recursively).
+ */
+ rc = RegDeleteKeyA(hkeyClassesRoot, g_apszTypelibGuidKeys[i]);
+ Assert(rc == ERROR_SUCCESS);
+ }
+ }
+}
+
+
+/**
+ * Hack to clean out obsolete typelibs on development boxes and such.
+ */
+static void removeOldMess(void)
+{
+ /*
+ * The standard location.
+ */
+ removeOldTypelib(HKEY_CLASSES_ROOT);
+ removeOldInterfaces(HKEY_CLASSES_ROOT);
+ removeOldClassIDs(HKEY_CLASSES_ROOT);
+
+ /*
+ * Wow64 if present.
+ */
+ HKEY hkeyWow64;
+ LONG rc = RegOpenKeyExA(HKEY_CLASSES_ROOT, "Wow6432Node", NULL,
+ DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &hkeyWow64);
+ if (rc == ERROR_SUCCESS)
+ {
+ removeOldTypelib(hkeyWow64);
+ removeOldInterfaces(hkeyWow64);
+ removeOldClassIDs(hkeyWow64);
+
+ RegCloseKey(hkeyWow64);
+ }
+}
+
+#endif /* WITH_MANUAL_CLEANUP */
+
diff --git a/src/VBox/Main/src-client/xpcom/module.cpp b/src/VBox/Main/src-client/xpcom/module.cpp
index e646a3bd..f66603d6 100644
--- a/src/VBox/Main/src-client/xpcom/module.cpp
+++ b/src/VBox/Main/src-client/xpcom/module.cpp
@@ -48,7 +48,6 @@
#include "MouseImpl.h"
#include "NATEngineImpl.h"
#include "NetworkAdapterImpl.h"
-#include "ProgressCombinedImpl.h"
#include "ProgressImpl.h"
#include "RemoteUSBDeviceImpl.h"
#include "SessionImpl.h"
@@ -60,9 +59,15 @@
// XPCOM glue code unfolding
+NS_DECL_CLASSINFO(VirtualBoxClient)
+NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VirtualBoxClient, IVirtualBoxClient)
+NS_DECL_CLASSINFO(Session)
+NS_IMPL_THREADSAFE_ISUPPORTS2_CI(Session, ISession, IInternalSessionControl)
+
+#ifndef VBOX_COM_INPROC_API_CLIENT
NS_DECL_CLASSINFO(Guest)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Guest, IGuest)
-#ifdef VBOX_WITH_GUEST_CONTROL
+ #ifdef VBOX_WITH_GUEST_CONTROL
NS_DECL_CLASSINFO(GuestDirectory)
NS_IMPL_THREADSAFE_ISUPPORTS2_CI(GuestDirectory, IGuestDirectory, IDirectory)
NS_DECL_CLASSINFO(GuestFile)
@@ -73,7 +78,7 @@ NS_DECL_CLASSINFO(GuestProcess)
NS_IMPL_THREADSAFE_ISUPPORTS2_CI(GuestProcess, IGuestProcess, IProcess)
NS_DECL_CLASSINFO(GuestSession)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(GuestSession, IGuestSession)
-#endif
+ #endif
NS_DECL_CLASSINFO(Keyboard)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Keyboard, IKeyboard)
NS_DECL_CLASSINFO(Mouse)
@@ -84,8 +89,6 @@ NS_DECL_CLASSINFO(MachineDebugger)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(MachineDebugger, IMachineDebugger)
NS_DECL_CLASSINFO(Progress)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Progress, IProgress)
-NS_DECL_CLASSINFO(CombinedProgress)
-NS_IMPL_THREADSAFE_ISUPPORTS1_CI(CombinedProgress, IProgress)
NS_DECL_CLASSINFO(OUSBDevice)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(OUSBDevice, IUSBDevice)
NS_DECL_CLASSINFO(RemoteUSBDevice)
@@ -94,24 +97,22 @@ NS_DECL_CLASSINFO(SharedFolder)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SharedFolder, ISharedFolder)
NS_DECL_CLASSINFO(VRDEServerInfo)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VRDEServerInfo, IVRDEServerInfo)
-#ifdef VBOX_WITH_EXTPACK
-NS_DECL_CLASSINFO(ExtPackFile)
-NS_IMPL_THREADSAFE_ISUPPORTS2_CI(ExtPackFile, IExtPackFile, IExtPackBase)
+ #ifdef VBOX_WITH_EXTPACK
+// deliberately omit ExtPackFile as it's unusable in the client context
+// NS_DECL_CLASSINFO(ExtPackFile)
+// NS_IMPL_THREADSAFE_ISUPPORTS2_CI(ExtPackFile, IExtPackFile, IExtPackBase)
NS_DECL_CLASSINFO(ExtPack)
NS_IMPL_THREADSAFE_ISUPPORTS2_CI(ExtPack, IExtPack, IExtPackBase)
NS_DECL_CLASSINFO(ExtPackManager)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ExtPackManager, IExtPackManager)
-#endif
+ #endif
NS_DECL_CLASSINFO(AdditionsFacility)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(AdditionsFacility, IAdditionsFacility)
-NS_DECL_CLASSINFO(Session)
-NS_IMPL_THREADSAFE_ISUPPORTS2_CI(Session, ISession, IInternalSessionControl)
NS_DECL_CLASSINFO(Console)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Console, IConsole)
-NS_DECL_CLASSINFO(VirtualBoxClient)
-NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VirtualBoxClient, IVirtualBoxClient)
+#endif /* VBOX_COM_INPROC_API_CLIENT */
/**
* Singleton class factory that holds a reference to the created instance