diff options
Diffstat (limited to 'src/VBox/Main/src-client')
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 |