From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Main/glue/AutoLock.cpp | 795 ++++ src/VBox/Main/glue/ErrorInfo.cpp | 374 ++ src/VBox/Main/glue/EventQueue.cpp | 257 ++ src/VBox/Main/glue/GetVBoxUserHomeDirectory.cpp | 142 + src/VBox/Main/glue/Makefile.kup | 0 src/VBox/Main/glue/NativeEventQueue.cpp | 675 +++ src/VBox/Main/glue/VBoxLogRelCreate.cpp | 214 + src/VBox/Main/glue/com.cpp | 185 + src/VBox/Main/glue/constants-python.xsl | 198 + src/VBox/Main/glue/errorprint.cpp | 222 + src/VBox/Main/glue/glue-java.xsl | 5259 +++++++++++++++++++++++ src/VBox/Main/glue/initterm.cpp | 861 ++++ src/VBox/Main/glue/string-base64.cpp | 61 + src/VBox/Main/glue/string.cpp | 1037 +++++ src/VBox/Main/glue/tests/Makefile | 81 + src/VBox/Main/glue/tests/TestVBox.java | 310 ++ src/VBox/Main/glue/tests/TestVBoxNATEngine.java | 206 + src/VBox/Main/glue/vbox-err-consts.sed | 59 + src/VBox/Main/glue/vboxapi.py | 1294 ++++++ src/VBox/Main/glue/xpcom/Makefile.kup | 0 src/VBox/Main/glue/xpcom/helpers.cpp | 204 + 21 files changed, 12434 insertions(+) create mode 100644 src/VBox/Main/glue/AutoLock.cpp create mode 100644 src/VBox/Main/glue/ErrorInfo.cpp create mode 100644 src/VBox/Main/glue/EventQueue.cpp create mode 100644 src/VBox/Main/glue/GetVBoxUserHomeDirectory.cpp create mode 100644 src/VBox/Main/glue/Makefile.kup create mode 100644 src/VBox/Main/glue/NativeEventQueue.cpp create mode 100644 src/VBox/Main/glue/VBoxLogRelCreate.cpp create mode 100644 src/VBox/Main/glue/com.cpp create mode 100755 src/VBox/Main/glue/constants-python.xsl create mode 100644 src/VBox/Main/glue/errorprint.cpp create mode 100644 src/VBox/Main/glue/glue-java.xsl create mode 100644 src/VBox/Main/glue/initterm.cpp create mode 100644 src/VBox/Main/glue/string-base64.cpp create mode 100644 src/VBox/Main/glue/string.cpp create mode 100644 src/VBox/Main/glue/tests/Makefile create mode 100644 src/VBox/Main/glue/tests/TestVBox.java create mode 100644 src/VBox/Main/glue/tests/TestVBoxNATEngine.java create mode 100644 src/VBox/Main/glue/vbox-err-consts.sed create mode 100755 src/VBox/Main/glue/vboxapi.py create mode 100644 src/VBox/Main/glue/xpcom/Makefile.kup create mode 100644 src/VBox/Main/glue/xpcom/helpers.cpp (limited to 'src/VBox/Main/glue') diff --git a/src/VBox/Main/glue/AutoLock.cpp b/src/VBox/Main/glue/AutoLock.cpp new file mode 100644 index 00000000..bc7b6eff --- /dev/null +++ b/src/VBox/Main/glue/AutoLock.cpp @@ -0,0 +1,795 @@ +/* $Id: AutoLock.cpp $ */ +/** @file + * Automatic locks, implementation. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define GLUE_USE_CRITSECTRW + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include +#include + +#include +#include + +#if defined(RT_LOCK_STRICT) +# include // for ASMReturnAddress +#endif + +#include +#include +#include + +#include "VBox/com/AutoLock.h" +#include + +#include +#include +#include + + +namespace util +{ + +//////////////////////////////////////////////////////////////////////////////// +// +// RuntimeLockClass +// +//////////////////////////////////////////////////////////////////////////////// + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION +typedef std::map LockValidationClassesMap; +LockValidationClassesMap g_mapLockValidationClasses; +#endif + +/** + * Called from initterm.cpp on process initialization (on the main thread) + * to give us a chance to initialize lock validation runtime data. + */ +void InitAutoLockSystem() +{ +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + struct + { + VBoxLockingClass cls; + const char *pcszDescription; + } aClasses[] = + { + { LOCKCLASS_VIRTUALBOXOBJECT, "2-VIRTUALBOXOBJECT" }, + { LOCKCLASS_HOSTOBJECT, "3-HOSTOBJECT" }, + { LOCKCLASS_LISTOFMACHINES, "4-LISTOFMACHINES" }, + { LOCKCLASS_MACHINEOBJECT, "5-MACHINEOBJECT" }, + { LOCKCLASS_SNAPSHOTOBJECT, "6-SNAPSHOTOBJECT" }, + { LOCKCLASS_MEDIUMQUERY, "7-MEDIUMQUERY" }, + { LOCKCLASS_LISTOFMEDIA, "8-LISTOFMEDIA" }, + { LOCKCLASS_LISTOFOTHEROBJECTS, "9-LISTOFOTHEROBJECTS" }, + { LOCKCLASS_OTHEROBJECT, "10-OTHEROBJECT" }, + { LOCKCLASS_PROGRESSLIST, "11-PROGRESSLIST" }, + { LOCKCLASS_OBJECTSTATE, "12-OBJECTSTATE" }, + { LOCKCLASS_TRANSLATOR, "13-TRANSLATOR" } + }; + + RTLOCKVALCLASS hClass; + int vrc; + for (unsigned i = 0; i < RT_ELEMENTS(aClasses); ++i) + { + vrc = RTLockValidatorClassCreate(&hClass, + true, /*fAutodidact*/ + RT_SRC_POS, + aClasses[i].pcszDescription); + AssertRC(vrc); + + // teach the new class that the classes created previously can be held + // while the new class is being acquired + for (LockValidationClassesMap::iterator it = g_mapLockValidationClasses.begin(); + it != g_mapLockValidationClasses.end(); + ++it) + { + RTLOCKVALCLASS &canBeHeld = it->second; + vrc = RTLockValidatorClassAddPriorClass(hClass, + canBeHeld); + AssertRC(vrc); + } + + // and store the new class + g_mapLockValidationClasses[aClasses[i].cls] = hClass; + } + +/* WriteLockHandle critsect1(LOCKCLASS_VIRTUALBOXOBJECT); + WriteLockHandle critsect2(LOCKCLASS_VIRTUALBOXLIST); + + AutoWriteLock lock1(critsect1 COMMA_LOCKVAL_SRC_POS); + AutoWriteLock lock2(critsect2 COMMA_LOCKVAL_SRC_POS);*/ +#endif +} + +bool AutoLockHoldsLocksInClass(VBoxLockingClass lockClass) +{ +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + return RTLockValidatorHoldsLocksInClass(NIL_RTTHREAD, g_mapLockValidationClasses[lockClass]); +#else + RT_NOREF(lockClass); + return false; +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// +// RWLockHandle +// +//////////////////////////////////////////////////////////////////////////////// + +struct RWLockHandle::Data +{ + Data() + { } + +#ifdef GLUE_USE_CRITSECTRW + mutable RTCRITSECTRW CritSect; +#else + RTSEMRW sem; +#endif + VBoxLockingClass lockClass; + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + com::Utf8Str strDescription; +#endif +}; + +RWLockHandle::RWLockHandle(VBoxLockingClass lockClass) +{ + m = new Data(); + + m->lockClass = lockClass; +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + m->strDescription.printf("r/w %RCv", this); +#endif + +#ifdef GLUE_USE_CRITSECTRW +# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + int vrc = RTCritSectRwInitEx(&m->CritSect, 0 /*fFlags*/, g_mapLockValidationClasses[lockClass], RTLOCKVAL_SUB_CLASS_ANY, NULL); +# else + int vrc = RTCritSectRwInitEx(&m->CritSect, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_ANY, NULL); +# endif +#else +# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + int vrc = RTSemRWCreateEx(&m->sem, 0 /*fFlags*/, g_mapLockValidationClasses[lockClass], RTLOCKVAL_SUB_CLASS_ANY, NULL); +# else + int vrc = RTSemRWCreateEx(&m->sem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_ANY, NULL); +# endif +#endif + AssertRC(vrc); +} + +/*virtual*/ RWLockHandle::~RWLockHandle() +{ +#ifdef GLUE_USE_CRITSECTRW + RTCritSectRwDelete(&m->CritSect); +#else + RTSemRWDestroy(m->sem); +#endif + delete m; +} + +/*virtual*/ bool RWLockHandle::isWriteLockOnCurrentThread() const +{ +#ifdef GLUE_USE_CRITSECTRW + return RTCritSectRwIsWriteOwner(&m->CritSect); +#else + return RTSemRWIsWriteOwner(m->sem); +#endif +} + +/*virtual*/ void RWLockHandle::lockWrite(LOCKVAL_SRC_POS_DECL) +{ +#ifdef GLUE_USE_CRITSECTRW +# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + int vrc = RTCritSectRwEnterExclDebug(&m->CritSect, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS); +# else + int vrc = RTCritSectRwEnterExcl(&m->CritSect); +# endif +#else +# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + int vrc = RTSemRWRequestWriteDebug(m->sem, RT_INDEFINITE_WAIT, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS); +# else + int vrc = RTSemRWRequestWrite(m->sem, RT_INDEFINITE_WAIT); +# endif +#endif + AssertRC(vrc); +} + +/*virtual*/ void RWLockHandle::unlockWrite() +{ +#ifdef GLUE_USE_CRITSECTRW + int vrc = RTCritSectRwLeaveExcl(&m->CritSect); +#else + int vrc = RTSemRWReleaseWrite(m->sem); +#endif + AssertRC(vrc); + +} + +/*virtual*/ bool RWLockHandle::isReadLockedOnCurrentThread(bool fWannaHear) const +{ +#ifdef GLUE_USE_CRITSECTRW + return RTCritSectRwIsReadOwner(&m->CritSect, fWannaHear); +#else + return RTSemRWIsReadOwner(m->sem, fWannaHear); +#endif +} + +/*virtual*/ void RWLockHandle::lockRead(LOCKVAL_SRC_POS_DECL) +{ +#ifdef GLUE_USE_CRITSECTRW +# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + int vrc = RTCritSectRwEnterSharedDebug(&m->CritSect, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS); +# else + int vrc = RTCritSectRwEnterShared(&m->CritSect); +# endif +#else +# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + int vrc = RTSemRWRequestReadDebug(m->sem, RT_INDEFINITE_WAIT, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS); +# else + int vrc = RTSemRWRequestRead(m->sem, RT_INDEFINITE_WAIT); +# endif +#endif + AssertRC(vrc); +} + +/*virtual*/ void RWLockHandle::unlockRead() +{ +#ifdef GLUE_USE_CRITSECTRW + int vrc = RTCritSectRwLeaveShared(&m->CritSect); +#else + int vrc = RTSemRWReleaseRead(m->sem); +#endif + AssertRC(vrc); +} + +/*virtual*/ uint32_t RWLockHandle::writeLockLevel() const +{ + /* Note! This does not include read recursions done by the writer! */ +#ifdef GLUE_USE_CRITSECTRW + return RTCritSectRwGetWriteRecursion(&m->CritSect); +#else + return RTSemRWGetWriteRecursion(m->sem); +#endif +} + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION +/*virtual*/ const char* RWLockHandle::describe() const +{ + return m->strDescription.c_str(); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// +// WriteLockHandle +// +//////////////////////////////////////////////////////////////////////////////// + +struct WriteLockHandle::Data +{ + Data() + { } + + mutable RTCRITSECT sem; + VBoxLockingClass lockClass; + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + com::Utf8Str strDescription; +#endif +}; + +WriteLockHandle::WriteLockHandle(VBoxLockingClass lockClass) +{ + m = new Data; + + m->lockClass = lockClass; + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + m->strDescription = com::Utf8StrFmt("crit %RCv", this); + int vrc = RTCritSectInitEx(&m->sem, 0/*fFlags*/, g_mapLockValidationClasses[lockClass], RTLOCKVAL_SUB_CLASS_ANY, NULL); +#else + int vrc = RTCritSectInitEx(&m->sem, 0/*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_ANY, NULL); +#endif + AssertRC(vrc); +} + +WriteLockHandle::~WriteLockHandle() +{ + RTCritSectDelete(&m->sem); + delete m; +} + +/*virtual*/ bool WriteLockHandle::isWriteLockOnCurrentThread() const +{ + return RTCritSectIsOwner(&m->sem); +} + +/*virtual*/ void WriteLockHandle::lockWrite(LOCKVAL_SRC_POS_DECL) +{ +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + RTCritSectEnterDebug(&m->sem, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS); +#else + RTCritSectEnter(&m->sem); +#endif +} + +/*virtual*/ bool WriteLockHandle::isReadLockedOnCurrentThread(bool fWannaHear) const +{ + RT_NOREF(fWannaHear); + return RTCritSectIsOwner(&m->sem); +} + +/*virtual*/ void WriteLockHandle::unlockWrite() +{ + RTCritSectLeave(&m->sem); +} + +/*virtual*/ void WriteLockHandle::lockRead(LOCKVAL_SRC_POS_DECL) +{ + lockWrite(LOCKVAL_SRC_POS_ARGS); +} + +/*virtual*/ void WriteLockHandle::unlockRead() +{ + unlockWrite(); +} + +/*virtual*/ uint32_t WriteLockHandle::writeLockLevel() const +{ + return RTCritSectGetRecursion(&m->sem); +} + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION +/*virtual*/ const char* WriteLockHandle::describe() const +{ + return m->strDescription.c_str(); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoLockBase +// +//////////////////////////////////////////////////////////////////////////////// + +typedef std::vector HandlesVector; + +struct AutoLockBase::Data +{ + Data(size_t cHandles +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + , const char *pcszFile_, + unsigned uLine_, + const char *pcszFunction_ +#endif + ) + : fIsLocked(false), + aHandles(cHandles) // size of array +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + , pcszFile(pcszFile_), + uLine(uLine_), + pcszFunction(pcszFunction_) +#endif + { + for (uint32_t i = 0; i < cHandles; ++i) + aHandles[i] = NULL; + } + + bool fIsLocked; // if true, then all items in aHandles are locked by this AutoLock and + // need to be unlocked in the destructor + HandlesVector aHandles; // array (vector) of LockHandle instances; in the case of AutoWriteLock + // and AutoReadLock, there will only be one item on the list; with the + // AutoMulti* derivatives, there will be multiple + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + // information about where the lock occurred (passed down from the AutoLock classes) + const char *pcszFile; + unsigned uLine; + const char *pcszFunction; +#endif +}; + +AutoLockBase::AutoLockBase(uint32_t cHandles + COMMA_LOCKVAL_SRC_POS_DECL) +{ + m = new Data(cHandles COMMA_LOCKVAL_SRC_POS_ARGS); +} + +AutoLockBase::AutoLockBase(uint32_t cHandles, + LockHandle *pHandle + COMMA_LOCKVAL_SRC_POS_DECL) +{ + Assert(cHandles == 1); NOREF(cHandles); + m = new Data(1 COMMA_LOCKVAL_SRC_POS_ARGS); + m->aHandles[0] = pHandle; +} + +AutoLockBase::~AutoLockBase() +{ + delete m; +} + +/** + * Requests ownership of all contained lock handles by calling + * the pure virtual callLockImpl() function on each of them, + * which must be implemented by the descendant class; in the + * implementation, AutoWriteLock will request a write lock + * whereas AutoReadLock will request a read lock. + * + * Does *not* modify the lock counts in the member variables. + */ +void AutoLockBase::callLockOnAllHandles() +{ + for (HandlesVector::iterator it = m->aHandles.begin(); + it != m->aHandles.end(); + ++it) + { + LockHandle *pHandle = *it; + if (pHandle) + // call virtual function implemented in AutoWriteLock or AutoReadLock + this->callLockImpl(*pHandle); + } +} + +/** + * Releases ownership of all contained lock handles by calling + * the pure virtual callUnlockImpl() function on each of them, + * which must be implemented by the descendant class; in the + * implementation, AutoWriteLock will release a write lock + * whereas AutoReadLock will release a read lock. + * + * Does *not* modify the lock counts in the member variables. + */ +void AutoLockBase::callUnlockOnAllHandles() +{ + // unlock in reverse order! + for (HandlesVector::reverse_iterator it = m->aHandles.rbegin(); + it != m->aHandles.rend(); + ++it) + { + LockHandle *pHandle = *it; + if (pHandle) + // call virtual function implemented in AutoWriteLock or AutoReadLock + this->callUnlockImpl(*pHandle); + } +} + +/** + * Destructor implementation that can also be called explicitly, if required. + * Restores the exact state before the AutoLock was created; that is, unlocks + * all contained semaphores. + */ +void AutoLockBase::cleanup() +{ + if (m->fIsLocked) + callUnlockOnAllHandles(); +} + +/** + * Requests ownership of all contained semaphores. Public method that can + * only be called once and that also gets called by the AutoLock constructors. + */ +void AutoLockBase::acquire() +{ + AssertMsgReturnVoid(!m->fIsLocked, ("m->fIsLocked is true, attempting to lock twice!")); + callLockOnAllHandles(); + m->fIsLocked = true; +} + +/** + * Releases ownership of all contained semaphores. Public method. + */ +void AutoLockBase::release() +{ + AssertMsgReturnVoid(m->fIsLocked, ("m->fIsLocked is false, cannot release!")); + callUnlockOnAllHandles(); + m->fIsLocked = false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoReadLock +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Release all read locks acquired by this instance through the #lock() + * call and destroys the instance. + * + * Note that if there there are nested #lock() calls without the + * corresponding number of #unlock() calls when the destructor is called, it + * will assert. This is because having an unbalanced number of nested locks + * is a program logic error which must be fixed. + */ +/*virtual*/ AutoReadLock::~AutoReadLock() +{ + LockHandle *pHandle = m->aHandles[0]; + + if (pHandle) + { + if (m->fIsLocked) + callUnlockImpl(*pHandle); + } +} + +/** + * Implementation of the pure virtual declared in AutoLockBase. + * This gets called by AutoLockBase.acquire() to actually request + * the semaphore; in the AutoReadLock implementation, we request + * the semaphore in read mode. + */ +/*virtual*/ void AutoReadLock::callLockImpl(LockHandle &l) +{ +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + l.lockRead(m->pcszFile, m->uLine, m->pcszFunction); +#else + l.lockRead(); +#endif +} + +/** + * Implementation of the pure virtual declared in AutoLockBase. + * This gets called by AutoLockBase.release() to actually release + * the semaphore; in the AutoReadLock implementation, we release + * the semaphore in read mode. + */ +/*virtual*/ void AutoReadLock::callUnlockImpl(LockHandle &l) +{ + l.unlockRead(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoWriteLockBase +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Implementation of the pure virtual declared in AutoLockBase. + * This gets called by AutoLockBase.acquire() to actually request + * the semaphore; in the AutoWriteLock implementation, we request + * the semaphore in write mode. + */ +/*virtual*/ void AutoWriteLockBase::callLockImpl(LockHandle &l) +{ +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + l.lockWrite(m->pcszFile, m->uLine, m->pcszFunction); +#else + l.lockWrite(); +#endif +} + +/** + * Implementation of the pure virtual declared in AutoLockBase. + * This gets called by AutoLockBase.release() to actually release + * the semaphore; in the AutoWriteLock implementation, we release + * the semaphore in write mode. + */ +/*virtual*/ void AutoWriteLockBase::callUnlockImpl(LockHandle &l) +{ + l.unlockWrite(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoWriteLock +// +//////////////////////////////////////////////////////////////////////////////// + +AutoWriteLock::AutoWriteLock(uint32_t cHandles, + LockHandle** pHandles + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(cHandles + COMMA_LOCKVAL_SRC_POS_ARGS) +{ + Assert(cHandles); + Assert(pHandles); + + for (uint32_t i = 0; i < cHandles; ++i) + m->aHandles[i] = pHandles[i]; + + acquire(); +} + + + +/** + * Attaches another handle to this auto lock instance. + * + * The previous object's lock is completely released before the new one is + * acquired. The lock level of the new handle will be the same. This + * also means that if the lock was not acquired at all before #attach(), it + * will not be acquired on the new handle too. + * + * @param aHandle New handle to attach. + */ +void AutoWriteLock::attach(LockHandle *aHandle) +{ + LockHandle *pHandle = m->aHandles[0]; + + /* detect simple self-reattachment */ + if (pHandle != aHandle) + { + bool fWasLocked = m->fIsLocked; + + cleanup(); + + m->aHandles[0] = aHandle; + m->fIsLocked = fWasLocked; + + if (aHandle) + if (fWasLocked) + callLockImpl(*aHandle); + } +} + +/** + * Returns @c true if the current thread holds a write lock on the managed + * read/write semaphore. Returns @c false if the managed semaphore is @c + * NULL. + * + * @note Intended for debugging only. + */ +bool AutoWriteLock::isWriteLockOnCurrentThread() const +{ + return m->aHandles[0] ? m->aHandles[0]->isWriteLockOnCurrentThread() : false; +} + + /** + * Returns the current write lock level of the managed semaphore. The lock + * level determines the number of nested #lock() calls on the given + * semaphore handle. Returns @c 0 if the managed semaphore is @c + * NULL. + * + * Note that this call is valid only when the current thread owns a write + * lock on the given semaphore handle and will assert otherwise. + * + * @note Intended for debugging only. + */ +uint32_t AutoWriteLock::writeLockLevel() const +{ + return m->aHandles[0] ? m->aHandles[0]->writeLockLevel() : 0; +} + +/** + * Returns @c true if the current thread holds a write lock on the managed + * read/write semaphore. Returns @c false if the managed semaphore is @c + * NULL. + * + * @note Intended for debugging only (esp. considering fWannaHear). + */ +bool AutoWriteLock::isReadLockedOnCurrentThread(bool fWannaHear) const +{ + return m->aHandles[0] ? m->aHandles[0]->isReadLockedOnCurrentThread(fWannaHear) : false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoMultiWriteLock* +// +//////////////////////////////////////////////////////////////////////////////// + +AutoMultiWriteLock2::AutoMultiWriteLock2(Lockable *pl1, + Lockable *pl2 + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(2 + COMMA_LOCKVAL_SRC_POS_ARGS) +{ + if (pl1) + m->aHandles[0] = pl1->lockHandle(); + if (pl2) + m->aHandles[1] = pl2->lockHandle(); + acquire(); +} + +AutoMultiWriteLock2::AutoMultiWriteLock2(LockHandle *pl1, + LockHandle *pl2 + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(2 + COMMA_LOCKVAL_SRC_POS_ARGS) +{ + m->aHandles[0] = pl1; + m->aHandles[1] = pl2; + acquire(); +} + +AutoMultiWriteLock3::AutoMultiWriteLock3(Lockable *pl1, + Lockable *pl2, + Lockable *pl3 + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(3 + COMMA_LOCKVAL_SRC_POS_ARGS) +{ + if (pl1) + m->aHandles[0] = pl1->lockHandle(); + if (pl2) + m->aHandles[1] = pl2->lockHandle(); + if (pl3) + m->aHandles[2] = pl3->lockHandle(); + acquire(); +} + +AutoMultiWriteLock3::AutoMultiWriteLock3(LockHandle *pl1, + LockHandle *pl2, + LockHandle *pl3 + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(3 + COMMA_LOCKVAL_SRC_POS_ARGS) +{ + m->aHandles[0] = pl1; + m->aHandles[1] = pl2; + m->aHandles[2] = pl3; + acquire(); +} + +AutoMultiWriteLock4::AutoMultiWriteLock4(Lockable *pl1, + Lockable *pl2, + Lockable *pl3, + Lockable *pl4 + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(4 + COMMA_LOCKVAL_SRC_POS_ARGS) +{ + if (pl1) + m->aHandles[0] = pl1->lockHandle(); + if (pl2) + m->aHandles[1] = pl2->lockHandle(); + if (pl3) + m->aHandles[2] = pl3->lockHandle(); + if (pl4) + m->aHandles[3] = pl4->lockHandle(); + acquire(); +} + +AutoMultiWriteLock4::AutoMultiWriteLock4(LockHandle *pl1, + LockHandle *pl2, + LockHandle *pl3, + LockHandle *pl4 + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(4 + COMMA_LOCKVAL_SRC_POS_ARGS) +{ + m->aHandles[0] = pl1; + m->aHandles[1] = pl2; + m->aHandles[2] = pl3; + m->aHandles[3] = pl4; + acquire(); +} + +} /* namespace util */ +/* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/glue/ErrorInfo.cpp b/src/VBox/Main/glue/ErrorInfo.cpp new file mode 100644 index 00000000..1fffa14a --- /dev/null +++ b/src/VBox/Main/glue/ErrorInfo.cpp @@ -0,0 +1,374 @@ +/* $Id: ErrorInfo.cpp $ */ + +/** @file + * + * ErrorInfo class definition + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#if defined(VBOX_WITH_XPCOM) +# include +# include +# include +#endif + +#include "VBox/com/VirtualBox.h" +#include "VBox/com/ErrorInfo.h" +#include "VBox/com/assert.h" +#include "VBox/com/com.h" +#include "VBox/com/MultiResult.h" + +#include +#include + +#include + +namespace com +{ + +//////////////////////////////////////////////////////////////////////////////// +// +// ErrorInfo class +// +//////////////////////////////////////////////////////////////////////////////// + +HRESULT ErrorInfo::getVirtualBoxErrorInfo(ComPtr &pVirtualBoxErrorInfo) +{ + HRESULT hrc = S_OK; + if (mErrorInfo) + hrc = mErrorInfo.queryInterfaceTo(pVirtualBoxErrorInfo.asOutParam()); + else + pVirtualBoxErrorInfo.setNull(); + return hrc; +} + +void ErrorInfo::copyFrom(const ErrorInfo &x) +{ + mIsBasicAvailable = x.mIsBasicAvailable; + mIsFullAvailable = x.mIsFullAvailable; + + mResultCode = x.mResultCode; + mResultDetail = x.mResultDetail; + mInterfaceID = x.mInterfaceID; + mComponent = x.mComponent; + mText = x.mText; + + if (x.m_pNext != NULL) + m_pNext = new ErrorInfo(*x.m_pNext); + else + m_pNext = NULL; + + mInterfaceName = x.mInterfaceName; + mCalleeIID = x.mCalleeIID; + mCalleeName = x.mCalleeName; + + mErrorInfo = x.mErrorInfo; +} + +void ErrorInfo::cleanup() +{ + mIsBasicAvailable = false; + mIsFullAvailable = false; + + if (m_pNext) + { + delete m_pNext; + m_pNext = NULL; + } + + mResultCode = S_OK; + mResultDetail = 0; + mInterfaceID.clear(); + mComponent.setNull(); + mText.setNull(); + mInterfaceName.setNull(); + mCalleeIID.clear(); + mCalleeName.setNull(); + mErrorInfo.setNull(); +} + +void ErrorInfo::init(bool aKeepObj /* = false */) +{ + HRESULT hrc = E_FAIL; + +#if !defined(VBOX_WITH_XPCOM) + + ComPtr err; + hrc = ::GetErrorInfo(0, err.asOutParam()); + if (hrc == S_OK && err) + { + if (aKeepObj) + mErrorInfo = err; + + ComPtr info; + hrc = err.queryInterfaceTo(info.asOutParam()); + if (SUCCEEDED(hrc) && info) + init(info); + + if (!mIsFullAvailable) + { + bool gotSomething = false; + + hrc = err->GetGUID(mInterfaceID.asOutParam()); + gotSomething |= SUCCEEDED(hrc); + if (SUCCEEDED(hrc)) + GetInterfaceNameByIID(mInterfaceID.ref(), mInterfaceName.asOutParam()); + + hrc = err->GetSource(mComponent.asOutParam()); + gotSomething |= SUCCEEDED(hrc); + + hrc = err->GetDescription(mText.asOutParam()); + gotSomething |= SUCCEEDED(hrc); + + if (gotSomething) + mIsBasicAvailable = true; + + AssertMsg(gotSomething, ("Nothing to fetch!\n")); + } + } + +#else // defined(VBOX_WITH_XPCOM) + + nsCOMPtr es; + es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &hrc); + if (NS_SUCCEEDED(hrc)) + { + nsCOMPtr em; + hrc = es->GetCurrentExceptionManager(getter_AddRefs(em)); + if (NS_SUCCEEDED(hrc)) + { + ComPtr ex; + hrc = em->GetCurrentException(ex.asOutParam()); + if (NS_SUCCEEDED(hrc) && ex) + { + if (aKeepObj) + mErrorInfo = ex; + + ComPtr info; + hrc = ex.queryInterfaceTo(info.asOutParam()); + if (NS_SUCCEEDED(hrc) && info) + init(info); + + if (!mIsFullAvailable) + { + bool gotSomething = false; + + hrc = ex->GetResult(&mResultCode); + gotSomething |= NS_SUCCEEDED(hrc); + + char *pszMsg; + hrc = ex->GetMessage(&pszMsg); + gotSomething |= NS_SUCCEEDED(hrc); + if (NS_SUCCEEDED(hrc)) + { + mText = Bstr(pszMsg); + nsMemory::Free(pszMsg); + } + + if (gotSomething) + mIsBasicAvailable = true; + + AssertMsg(gotSomething, ("Nothing to fetch!\n")); + } + + // set the exception to NULL (to emulate Win32 behavior) + em->SetCurrentException(NULL); + + hrc = NS_OK; + } + } + } + /* Ignore failure when called after nsComponentManagerImpl::Shutdown(). */ + else if (hrc == NS_ERROR_UNEXPECTED) + hrc = NS_OK; + + AssertComRC(hrc); + +#endif // defined(VBOX_WITH_XPCOM) +} + +void ErrorInfo::init(IUnknown *aI, + const GUID &aIID, + bool aKeepObj /* = false */) +{ + AssertReturnVoid(aI); + +#if !defined(VBOX_WITH_XPCOM) + + ComPtr iface = aI; + ComPtr serr; + HRESULT hrc = iface.queryInterfaceTo(serr.asOutParam()); + if (SUCCEEDED(hrc)) + { + hrc = serr->InterfaceSupportsErrorInfo(aIID); + if (SUCCEEDED(hrc)) + init(aKeepObj); + } + +#else + + init(aKeepObj); + +#endif + + if (mIsBasicAvailable) + { + mCalleeIID = aIID; + GetInterfaceNameByIID(aIID, mCalleeName.asOutParam()); + } +} + +void ErrorInfo::init(IVirtualBoxErrorInfo *info) +{ + AssertReturnVoid(info); + + HRESULT hrc = E_FAIL; + bool gotSomething = false; + bool gotAll = true; + LONG lrc, lrd; + + hrc = info->COMGETTER(ResultCode)(&lrc); mResultCode = lrc; + gotSomething |= SUCCEEDED(hrc); + gotAll &= SUCCEEDED(hrc); + + hrc = info->COMGETTER(ResultDetail)(&lrd); mResultDetail = lrd; + gotSomething |= SUCCEEDED(hrc); + gotAll &= SUCCEEDED(hrc); + + Bstr iid; + hrc = info->COMGETTER(InterfaceID)(iid.asOutParam()); + gotSomething |= SUCCEEDED(hrc); + gotAll &= SUCCEEDED(hrc); + if (SUCCEEDED(hrc)) + { + mInterfaceID = iid; + GetInterfaceNameByIID(mInterfaceID.ref(), mInterfaceName.asOutParam()); + } + + hrc = info->COMGETTER(Component)(mComponent.asOutParam()); + gotSomething |= SUCCEEDED(hrc); + gotAll &= SUCCEEDED(hrc); + + hrc = info->COMGETTER(Text)(mText.asOutParam()); + gotSomething |= SUCCEEDED(hrc); + gotAll &= SUCCEEDED(hrc); + + m_pNext = NULL; + + ComPtr next; + hrc = info->COMGETTER(Next)(next.asOutParam()); + if (SUCCEEDED(hrc) && !next.isNull()) + { + m_pNext = new ErrorInfo(next); + Assert(m_pNext != NULL); + if (!m_pNext) + hrc = E_OUTOFMEMORY; + } + + gotSomething |= SUCCEEDED(hrc); + gotAll &= SUCCEEDED(hrc); + + mIsBasicAvailable = gotSomething; + mIsFullAvailable = gotAll; + + mErrorInfo = info; + + AssertMsg(gotSomething, ("Nothing to fetch!\n")); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ProgressErrorInfo class +// +//////////////////////////////////////////////////////////////////////////////// + +ProgressErrorInfo::ProgressErrorInfo(IProgress *progress) : + ErrorInfo(false /* aDummy */) +{ + Assert(progress); + if (!progress) + return; + + ComPtr info; + HRESULT hrc = progress->COMGETTER(ErrorInfo)(info.asOutParam()); + if (SUCCEEDED(hrc) && info) + init(info); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ErrorInfoKeeper class +// +//////////////////////////////////////////////////////////////////////////////// + +HRESULT ErrorInfoKeeper::restore() +{ + if (mForgot) + return S_OK; + + HRESULT hrc = S_OK; + +#if !defined(VBOX_WITH_XPCOM) + + ComPtr err; + if (!mErrorInfo.isNull()) + { + hrc = mErrorInfo.queryInterfaceTo(err.asOutParam()); + AssertComRC(hrc); + } + hrc = ::SetErrorInfo(0, err); + +#else // defined(VBOX_WITH_XPCOM) + + nsCOMPtr es; + es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &hrc); + if (NS_SUCCEEDED(hrc)) + { + nsCOMPtr em; + hrc = es->GetCurrentExceptionManager(getter_AddRefs(em)); + if (NS_SUCCEEDED(hrc)) + { + ComPtr ex; + if (!mErrorInfo.isNull()) + { + hrc = mErrorInfo.queryInterfaceTo(ex.asOutParam()); + AssertComRC(hrc); + } + hrc = em->SetCurrentException(ex); + } + } + +#endif // defined(VBOX_WITH_XPCOM) + + if (SUCCEEDED(hrc)) + { + mErrorInfo.setNull(); + mForgot = true; + } + + return hrc; +} + +} /* namespace com */ + diff --git a/src/VBox/Main/glue/EventQueue.cpp b/src/VBox/Main/glue/EventQueue.cpp new file mode 100644 index 00000000..a8a6fad1 --- /dev/null +++ b/src/VBox/Main/glue/EventQueue.cpp @@ -0,0 +1,257 @@ +/* $Id: EventQueue.cpp $ */ +/** @file + * Event queue class declaration. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +/** @todo Adapt / update documentation! */ + +#include "VBox/com/EventQueue.h" + +#include +#include /* For bad_alloc. */ + +#include +#include +#include +#include +#include + +namespace com +{ + +// EventQueue class +//////////////////////////////////////////////////////////////////////////////// + +EventQueue::EventQueue(void) + : mUserCnt(0), + mShutdown(false) +{ + int vrc = RTCritSectInit(&mCritSect); + AssertRC(vrc); + + vrc = RTSemEventCreate(&mSemEvent); + AssertRC(vrc); +} + +EventQueue::~EventQueue(void) +{ + int vrc = RTCritSectDelete(&mCritSect); + AssertRC(vrc); + + vrc = RTSemEventDestroy(mSemEvent); + AssertRC(vrc); + + EventQueueListIterator it = mEvents.begin(); + while (it != mEvents.end()) + { + (*it)->Release(); + it = mEvents.erase(it); + } +} + +/** + * Process events pending on this event queue, and wait up to given timeout, if + * nothing is available. + * + * Must be called on same thread this event queue was created on. + * + * @param cMsTimeout The timeout specified as milliseconds. Use + * RT_INDEFINITE_WAIT to wait till an event is posted on the + * queue. + * + * @returns VBox status code + * @retval VINF_SUCCESS if one or more messages was processed. + * @retval VERR_TIMEOUT if cMsTimeout expired. + * @retval VERR_INVALID_CONTEXT if called on the wrong thread. + * @retval VERR_INTERRUPTED if interruptEventQueueProcessing was called. + * On Windows will also be returned when WM_QUIT is encountered. + * On Darwin this may also be returned when the native queue is + * stopped or destroyed/finished. + * @retval VINF_INTERRUPTED if the native system call was interrupted by a + * an asynchronous event delivery (signal) or just felt like returning + * out of bounds. On darwin it will also be returned if the queue is + * stopped. + */ +int EventQueue::processEventQueue(RTMSINTERVAL cMsTimeout) +{ + size_t cNumEvents; + int vrc = RTCritSectEnter(&mCritSect); + if (RT_SUCCESS(vrc)) + { + if (mUserCnt == 0) /* No concurrent access allowed. */ + { + mUserCnt++; + + cNumEvents = mEvents.size(); + if (!cNumEvents) + { + int vrc2 = RTCritSectLeave(&mCritSect); + AssertRC(vrc2); + + vrc = RTSemEventWaitNoResume(mSemEvent, cMsTimeout); + + vrc2 = RTCritSectEnter(&mCritSect); + AssertRC(vrc2); + + if (RT_SUCCESS(vrc)) + { + if (mShutdown) + vrc = VERR_INTERRUPTED; + cNumEvents = mEvents.size(); + } + } + + if (RT_SUCCESS(vrc)) + vrc = processPendingEvents(cNumEvents); + + Assert(mUserCnt); + mUserCnt--; + } + else + vrc = VERR_WRONG_ORDER; + + int vrc2 = RTCritSectLeave(&mCritSect); + if (RT_SUCCESS(vrc)) + vrc = vrc2; + } + + Assert(vrc != VERR_TIMEOUT || cMsTimeout != RT_INDEFINITE_WAIT); + return vrc; +} + +/** + * Processes all pending events in the queue at the time of + * calling. Note: Does no initial locking, must be done by the + * caller! + * + * @return IPRT status code. + */ +int EventQueue::processPendingEvents(size_t cNumEvents) +{ + if (!cNumEvents) /* Nothing to process? Bail out early. */ + return VINF_SUCCESS; + + int vrc = VINF_SUCCESS; + + EventQueueListIterator it = mEvents.begin(); + for (size_t i = 0; + i < cNumEvents + && it != mEvents.end(); i++) + { + Event *pEvent = *it; + AssertPtr(pEvent); + + mEvents.erase(it); + + int vrc2 = RTCritSectLeave(&mCritSect); + AssertRC(vrc2); + + pEvent->handler(); + pEvent->Release(); + + vrc2 = RTCritSectEnter(&mCritSect); + AssertRC(vrc2); + + it = mEvents.begin(); + if (mShutdown) + { + vrc = VERR_INTERRUPTED; + break; + } + } + + return vrc; +} + +/** + * Interrupt thread waiting on event queue processing. + * + * Can be called on any thread. + * + * @returns VBox status code. + */ +int EventQueue::interruptEventQueueProcessing(void) +{ + ASMAtomicWriteBool(&mShutdown, true); + + return RTSemEventSignal(mSemEvent); +} + +/** + * Posts an event to this event loop asynchronously. + * + * @param pEvent the event to post, must be allocated using |new| + * @return TRUE if successful and false otherwise + */ +BOOL EventQueue::postEvent(Event *pEvent) +{ + int vrc = RTCritSectEnter(&mCritSect); + if (RT_SUCCESS(vrc)) + { + try + { + if (pEvent) + { + pEvent->AddRef(); + mEvents.push_back(pEvent); + } + else /* No locking, since we're already in our crit sect. */ + mShutdown = true; + + size_t cEvents = mEvents.size(); + if (cEvents > _1K) /** @todo Make value configurable? */ + { + static int s_cBitchedAboutLotEvents = 0; + if (s_cBitchedAboutLotEvents < 10) + LogRel(("Warning: Event queue received lots of events (%zu), expect delayed event handling (%d/10)\n", + cEvents, ++s_cBitchedAboutLotEvents)); + } + + /* Leave critical section before signalling event. */ + vrc = RTCritSectLeave(&mCritSect); + if (RT_SUCCESS(vrc)) + { + int vrc2 = RTSemEventSignal(mSemEvent); + AssertRC(vrc2); + } + } + catch (std::bad_alloc &ba) + { + NOREF(ba); + vrc = VERR_NO_MEMORY; + } + + if (RT_FAILURE(vrc)) + { + int vrc2 = RTCritSectLeave(&mCritSect); + AssertRC(vrc2); + } + } + + return RT_SUCCESS(vrc) ? TRUE : FALSE; +} + +} +/* namespace com */ diff --git a/src/VBox/Main/glue/GetVBoxUserHomeDirectory.cpp b/src/VBox/Main/glue/GetVBoxUserHomeDirectory.cpp new file mode 100644 index 00000000..cdfa6a01 --- /dev/null +++ b/src/VBox/Main/glue/GetVBoxUserHomeDirectory.cpp @@ -0,0 +1,142 @@ +/* $Id: GetVBoxUserHomeDirectory.cpp $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - GetVBoxUserHomeDirectory. + */ + +/* + * Copyright (C) 2005-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN +#include + +#include +#include +#include +#include +#include + +#include +#include + + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +#if !defined(RT_OS_DARWIN) && !defined(RT_OS_WINDOWS) +char g_szXdgConfigHome[RTPATH_MAX] = ""; +#endif + +/** + * Possible locations for the VirtualBox user configuration folder, + * listed from oldest (as in legacy) to newest. These can be either + * absolute or relative to the home directory. We use the first entry + * of the list which corresponds to a real folder on storage, or + * create a folder corresponding to the last in the list (the least + * legacy) if none do. + */ +const char * const g_apcszUserHome[] = +{ +#ifdef RT_OS_DARWIN + "Library/VirtualBox", +#elif defined RT_OS_WINDOWS + ".VirtualBox", +#else + ".VirtualBox", g_szXdgConfigHome, +#endif +}; + + +namespace com +{ + +static int composeHomePath(char *aDir, size_t aDirLen, const char *pcszBase) +{ + int vrc; + if (RTPathStartsWithRoot(pcszBase)) + vrc = RTStrCopy(aDir, aDirLen, pcszBase); + else + { + /* compose the config directory (full path) */ + /** @todo r=bird: RTPathUserHome doesn't necessarily return a + * full (abs) path like the comment above seems to indicate. */ + vrc = RTPathUserHome(aDir, aDirLen); + if (RT_SUCCESS(vrc)) + vrc = RTPathAppend(aDir, aDirLen, pcszBase); + } + return vrc; +} + +int GetVBoxUserHomeDirectory(char *aDir, size_t aDirLen, bool fCreateDir) +{ + AssertReturn(aDir, VERR_INVALID_POINTER); + AssertReturn(aDirLen > 0, VERR_BUFFER_OVERFLOW); + + /* start with null */ + *aDir = 0; + + char szTmp[RTPATH_MAX]; + int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_HOME", szTmp, sizeof(szTmp), NULL); + if (RT_SUCCESS(vrc) || vrc == VERR_ENV_VAR_NOT_FOUND) + { + bool fFound = false; + if (RT_SUCCESS(vrc)) + { + /* get the full path name */ + vrc = RTPathAbs(szTmp, aDir, aDirLen); + } + else + { +#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_DARWIN) + vrc = RTEnvGetEx(RTENV_DEFAULT, "XDG_CONFIG_HOME", g_szXdgConfigHome, sizeof(g_szXdgConfigHome), NULL); + if (RT_SUCCESS(vrc)) + vrc = RTPathAppend(g_szXdgConfigHome, sizeof(g_szXdgConfigHome), "VirtualBox"); + AssertMsg(vrc == VINF_SUCCESS || vrc == VERR_ENV_VAR_NOT_FOUND, ("%Rrc\n", vrc)); + if (RT_FAILURE_NP(vrc)) + vrc = RTStrCopy(g_szXdgConfigHome, sizeof(g_szXdgConfigHome), ".config/VirtualBox"); +#endif + for (unsigned i = 0; i < RT_ELEMENTS(g_apcszUserHome); ++i) + { + vrc = composeHomePath(aDir, aDirLen, g_apcszUserHome[i]); + if ( RT_SUCCESS(vrc) + && RTDirExists(aDir)) + { + fFound = true; + break; + } + } + } + + /* ensure the home directory exists */ + if (RT_SUCCESS(vrc)) + if (!fFound && fCreateDir) + vrc = RTDirCreateFullPath(aDir, 0700); + } + + return vrc; +} + +} /* namespace com */ diff --git a/src/VBox/Main/glue/Makefile.kup b/src/VBox/Main/glue/Makefile.kup new file mode 100644 index 00000000..e69de29b diff --git a/src/VBox/Main/glue/NativeEventQueue.cpp b/src/VBox/Main/glue/NativeEventQueue.cpp new file mode 100644 index 00000000..3f8b1e98 --- /dev/null +++ b/src/VBox/Main/glue/NativeEventQueue.cpp @@ -0,0 +1,675 @@ +/* $Id: NativeEventQueue.cpp $ */ +/** @file + * MS COM / XPCOM Abstraction Layer: + * Main event queue class declaration + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "VBox/com/NativeEventQueue.h" + +#include /* For bad_alloc. */ + +#ifdef RT_OS_DARWIN +# include +#endif + +#if defined(VBOX_WITH_XPCOM) && !defined(RT_OS_DARWIN) && !defined(RT_OS_OS2) +# define USE_XPCOM_QUEUE +#endif + +#include +#include +#include +#include +#ifdef USE_XPCOM_QUEUE +# include +#endif + +namespace com +{ + +// NativeEventQueue class +//////////////////////////////////////////////////////////////////////////////// + +#ifndef VBOX_WITH_XPCOM + +# define CHECK_THREAD_RET(ret) \ + do { \ + AssertMsg(GetCurrentThreadId() == mThreadId, ("Must be on event queue thread!")); \ + if (GetCurrentThreadId() != mThreadId) \ + return ret; \ + } while (0) + +/** Magic LPARAM value for the WM_USER messages that we're posting. + * @remarks This magic value is duplicated in + * vboxapi/PlatformMSCOM::interruptWaitEvents(). */ +#define EVENTQUEUE_WIN_LPARAM_MAGIC UINT32_C(0xf241b819) + + +#else // VBOX_WITH_XPCOM + +# define CHECK_THREAD_RET(ret) \ + do { \ + if (!mEventQ) \ + return ret; \ + BOOL isOnCurrentThread = FALSE; \ + mEventQ->IsOnCurrentThread(&isOnCurrentThread); \ + AssertMsg(isOnCurrentThread, ("Must be on event queue thread!")); \ + if (!isOnCurrentThread) \ + return ret; \ + } while (0) + +#endif // VBOX_WITH_XPCOM + +/** Pointer to the main event queue. */ +NativeEventQueue *NativeEventQueue::sMainQueue = NULL; + + +#ifdef VBOX_WITH_XPCOM + +struct MyPLEvent : public PLEvent +{ + MyPLEvent(NativeEvent *e) : event(e) {} + NativeEvent *event; +}; + +/* static */ +void *PR_CALLBACK com::NativeEventQueue::plEventHandler(PLEvent *self) +{ + NativeEvent *ev = ((MyPLEvent *)self)->event; + if (ev) + ev->handler(); + else + { + NativeEventQueue *eq = (NativeEventQueue *)self->owner; + Assert(eq); + eq->mInterrupted = true; + } + return NULL; +} + +/* static */ +void PR_CALLBACK com::NativeEventQueue::plEventDestructor(PLEvent *self) +{ + NativeEvent *ev = ((MyPLEvent *)self)->event; + if (ev) + delete ev; + delete self; +} + +#endif // VBOX_WITH_XPCOM + +/** + * Constructs an event queue for the current thread. + * + * Currently, there can be only one event queue per thread, so if an event + * queue for the current thread already exists, this object is simply attached + * to the existing event queue. + */ +NativeEventQueue::NativeEventQueue() +{ +#ifndef VBOX_WITH_XPCOM + + mThreadId = GetCurrentThreadId(); + // force the system to create the message queue for the current thread + MSG msg; + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + if (!DuplicateHandle(GetCurrentProcess(), + GetCurrentThread(), + GetCurrentProcess(), + &mhThread, + 0 /*dwDesiredAccess*/, + FALSE /*bInheritHandle*/, + DUPLICATE_SAME_ACCESS)) + mhThread = INVALID_HANDLE_VALUE; + +#else // VBOX_WITH_XPCOM + + mEQCreated = false; + mInterrupted = false; + + // Here we reference the global nsIEventQueueService instance and hold it + // until we're destroyed. This is necessary to keep NS_ShutdownXPCOM() away + // from calling StopAcceptingEvents() on all event queues upon destruction of + // nsIEventQueueService, and makes sense when, for some reason, this happens + // *before* we're able to send a NULL event to stop our event handler thread + // when doing unexpected cleanup caused indirectly by NS_ShutdownXPCOM() + // that is performing a global cleanup of everything. A good example of such + // situation is when NS_ShutdownXPCOM() is called while the VirtualBox component + // is still alive (because it is still referenced): eventually, it results in + // a VirtualBox::uninit() call from where it is already not possible to post + // NULL to the event thread (because it stopped accepting events). + + nsresult hrc = NS_GetEventQueueService(getter_AddRefs(mEventQService)); + + if (NS_SUCCEEDED(hrc)) + { + hrc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQ)); + if (hrc == NS_ERROR_NOT_AVAILABLE) + { + hrc = mEventQService->CreateThreadEventQueue(); + if (NS_SUCCEEDED(hrc)) + { + mEQCreated = true; + hrc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQ)); + } + } + } + AssertComRC(hrc); + +#endif // VBOX_WITH_XPCOM +} + +NativeEventQueue::~NativeEventQueue() +{ +#ifndef VBOX_WITH_XPCOM + if (mhThread != INVALID_HANDLE_VALUE) + { + CloseHandle(mhThread); + mhThread = INVALID_HANDLE_VALUE; + } +#else // VBOX_WITH_XPCOM + // process all pending events before destruction + if (mEventQ) + { + if (mEQCreated) + { + mEventQ->StopAcceptingEvents(); + mEventQ->ProcessPendingEvents(); + mEventQService->DestroyThreadEventQueue(); + } + mEventQ = nsnull; + mEventQService = nsnull; + } +#endif // VBOX_WITH_XPCOM +} + +/** + * Initializes the main event queue instance. + * @returns VBox status code. + * + * @remarks If you're using the rest of the COM/XPCOM glue library, + * com::Initialize() will take care of initializing and uninitializing + * the NativeEventQueue class. If you don't call com::Initialize, you must + * make sure to call this method on the same thread that did the + * XPCOM initialization or we'll end up using the wrong main queue. + */ +/* static */ +int NativeEventQueue::init() +{ + Assert(sMainQueue == NULL); + Assert(RTThreadIsMain(RTThreadSelf())); + + try + { + sMainQueue = new NativeEventQueue(); + AssertPtr(sMainQueue); +#ifdef VBOX_WITH_XPCOM + /* Check that it actually is the main event queue, i.e. that + we're called on the right thread. */ + nsCOMPtr q; + nsresult rv = NS_GetMainEventQ(getter_AddRefs(q)); + AssertComRCReturn(rv, VERR_INVALID_POINTER); + Assert(q == sMainQueue->mEventQ); + + /* Check that it's a native queue. */ + PRBool fIsNative = PR_FALSE; + rv = sMainQueue->mEventQ->IsQueueNative(&fIsNative); + Assert(NS_SUCCEEDED(rv) && fIsNative); +#endif // VBOX_WITH_XPCOM + } + catch (std::bad_alloc &ba) + { + NOREF(ba); + return VERR_NO_MEMORY; + } + + return VINF_SUCCESS; +} + +/** + * Uninitialize the global resources (i.e. the main event queue instance). + * @returns VINF_SUCCESS + */ +/* static */ +int NativeEventQueue::uninit() +{ + if (sMainQueue) + { + /* Must process all events to make sure that no NULL event is left + * after this point. It would need to modify the state of sMainQueue. */ +#ifdef RT_OS_DARWIN /* Do not process the native runloop, the toolkit may not be ready for it. */ + sMainQueue->mEventQ->ProcessPendingEvents(); +#else + sMainQueue->processEventQueue(0); +#endif + delete sMainQueue; + sMainQueue = NULL; + } + return VINF_SUCCESS; +} + +/** + * Get main event queue instance. + * + * Depends on init() being called first. + */ +/* static */ +NativeEventQueue* NativeEventQueue::getMainEventQueue() +{ + return sMainQueue; +} + +#ifdef VBOX_WITH_XPCOM +# ifdef RT_OS_DARWIN +/** + * Wait for events and process them (Darwin). + * + * @retval VINF_SUCCESS + * @retval VERR_TIMEOUT + * @retval VERR_INTERRUPTED + * + * @param cMsTimeout How long to wait, or RT_INDEFINITE_WAIT. + */ +static int waitForEventsOnDarwin(RTMSINTERVAL cMsTimeout) +{ + /* + * Wait for the requested time, if we get a hit we do a poll to process + * any other pending messages. + * + * Note! About 1.0e10: According to the sources anything above 3.1556952e+9 + * means indefinite wait and 1.0e10 is what CFRunLoopRun() uses. + */ + CFTimeInterval rdTimeout = cMsTimeout == RT_INDEFINITE_WAIT ? 1e10 : (double)cMsTimeout / 1000; + OSStatus orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/); + if (orc == kCFRunLoopRunHandledSource) + { + OSStatus orc2 = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/); + if ( orc2 == kCFRunLoopRunStopped + || orc2 == kCFRunLoopRunFinished) + orc = orc2; + } + if ( orc == 0 /*???*/ + || orc == kCFRunLoopRunHandledSource) + return VINF_SUCCESS; + if ( orc == kCFRunLoopRunStopped + || orc == kCFRunLoopRunFinished) + return VERR_INTERRUPTED; + AssertMsg(orc == kCFRunLoopRunTimedOut, ("Unexpected status code from CFRunLoopRunInMode: %#x", orc)); + return VERR_TIMEOUT; +} +# else // !RT_OS_DARWIN + +/** + * Wait for events (generic XPCOM). + * + * @retval VINF_SUCCESS + * @retval VERR_TIMEOUT + * @retval VINF_INTERRUPTED + * @retval VERR_INTERNAL_ERROR_4 + * + * @param pQueue The queue to wait on. + * @param cMsTimeout How long to wait, or RT_INDEFINITE_WAIT. + */ +static int waitForEventsOnXPCOM(nsIEventQueue *pQueue, RTMSINTERVAL cMsTimeout) +{ + int fd = pQueue->GetEventQueueSelectFD(); + fd_set fdsetR; + FD_ZERO(&fdsetR); + FD_SET(fd, &fdsetR); + + fd_set fdsetE = fdsetR; + + struct timeval tv = {0,0}; + struct timeval *ptv; + if (cMsTimeout == RT_INDEFINITE_WAIT) + ptv = NULL; + else + { + tv.tv_sec = cMsTimeout / 1000; + tv.tv_usec = (cMsTimeout % 1000) * 1000; + ptv = &tv; + } + + int iRc = select(fd + 1, &fdsetR, NULL, &fdsetE, ptv); + int vrc; + if (iRc > 0) + vrc = VINF_SUCCESS; + else if (iRc == 0) + vrc = VERR_TIMEOUT; + else if (errno == EINTR) + vrc = VINF_INTERRUPTED; + else + { + static uint32_t s_ErrorCount = 0; + if (s_ErrorCount < 500) + { + LogRel(("waitForEventsOnXPCOM iRc=%d errno=%d\n", iRc, errno)); + ++s_ErrorCount; + } + + AssertMsgFailed(("iRc=%d errno=%d\n", iRc, errno)); + vrc = VERR_INTERNAL_ERROR_4; + } + return vrc; +} + +# endif // !RT_OS_DARWIN +#endif // VBOX_WITH_XPCOM + +#ifndef VBOX_WITH_XPCOM + +/** + * Dispatch a message on Windows. + * + * This will pick out our events and handle them specially. + * + * @returns @a vrc or VERR_INTERRUPTED (WM_QUIT or NULL msg). + * @param pMsg The message to dispatch. + * @param vrc The current status code. + */ +/*static*/ +int NativeEventQueue::dispatchMessageOnWindows(MSG const *pMsg, int vrc) +{ + /* + * Check for and dispatch our events. + */ + if ( pMsg->hwnd == NULL + && pMsg->message == WM_USER) + { + if (pMsg->lParam == EVENTQUEUE_WIN_LPARAM_MAGIC) + { + NativeEvent *pEvent = (NativeEvent *)pMsg->wParam; + if (pEvent) + { + pEvent->handler(); + delete pEvent; + } + else + vrc = VERR_INTERRUPTED; + return vrc; + } + AssertMsgFailed(("lParam=%p wParam=%p\n", pMsg->lParam, pMsg->wParam)); + } + + /* + * Check for the quit message and dispatch the message the normal way. + */ + if (pMsg->message == WM_QUIT) + vrc = VERR_INTERRUPTED; + TranslateMessage(pMsg); + DispatchMessage(pMsg); + + return vrc; +} + + +/** + * Process pending events (Windows). + * + * @retval VINF_SUCCESS + * @retval VERR_TIMEOUT + * @retval VERR_INTERRUPTED. + */ +static int processPendingEvents(void) +{ + int vrc = VERR_TIMEOUT; + MSG Msg; + if (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE)) + { + vrc = VINF_SUCCESS; + do + vrc = NativeEventQueue::dispatchMessageOnWindows(&Msg, vrc); + while (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE)); + } + return vrc; +} + +#else // VBOX_WITH_XPCOM + +/** + * Process pending XPCOM events. + * @param pQueue The queue to process events on. + * @retval VINF_SUCCESS + * @retval VERR_TIMEOUT + * @retval VERR_INTERRUPTED (darwin only) + * @retval VERR_INTERNAL_ERROR_2 + */ +static int processPendingEvents(nsIEventQueue *pQueue) +{ + /* ProcessPendingEvents doesn't report back what it did, so check here. */ + PRBool fHasEvents = PR_FALSE; + nsresult hrc = pQueue->PendingEvents(&fHasEvents); + if (NS_FAILED(hrc)) + return VERR_INTERNAL_ERROR_2; + + /* Process pending events. */ + int vrc = VINF_SUCCESS; + if (fHasEvents) + pQueue->ProcessPendingEvents(); + else + vrc = VERR_TIMEOUT; + +# ifdef RT_OS_DARWIN + /* Process pending native events. */ + int vrc2 = waitForEventsOnDarwin(0); + if (vrc == VERR_TIMEOUT || vrc2 == VERR_INTERRUPTED) + vrc = vrc2; +# endif + + return vrc; +} + +#endif // VBOX_WITH_XPCOM + +/** + * Process events pending on this event queue, and wait up to given timeout, if + * nothing is available. + * + * Must be called on same thread this event queue was created on. + * + * @param cMsTimeout The timeout specified as milliseconds. Use + * RT_INDEFINITE_WAIT to wait till an event is posted on the + * queue. + * + * @returns VBox status code + * @retval VINF_SUCCESS if one or more messages was processed. + * @retval VERR_TIMEOUT if cMsTimeout expired. + * @retval VERR_INVALID_CONTEXT if called on the wrong thread. + * @retval VERR_INTERRUPTED if interruptEventQueueProcessing was called. + * On Windows will also be returned when WM_QUIT is encountered. + * On Darwin this may also be returned when the native queue is + * stopped or destroyed/finished. + * @retval VINF_INTERRUPTED if the native system call was interrupted by a + * an asynchronous event delivery (signal) or just felt like returning + * out of bounds. On darwin it will also be returned if the queue is + * stopped. + * + * @note On darwin this function will not return when the thread receives a + * signal, it will just resume the wait. + */ +int NativeEventQueue::processEventQueue(RTMSINTERVAL cMsTimeout) +{ + int vrc; + CHECK_THREAD_RET(VERR_INVALID_CONTEXT); + +#ifdef VBOX_WITH_XPCOM + /* + * Process pending events, if none are available and we're not in a + * poll call, wait for some to appear. (We have to be a little bit + * careful after waiting for the events since Darwin will process + * them as part of the wait, while the XPCOM case will not.) + * + * Note! Unfortunately, WaitForEvent isn't interruptible with Ctrl-C, + * while select() is. So we cannot use it for indefinite waits. + */ + vrc = processPendingEvents(mEventQ); + if ( vrc == VERR_TIMEOUT + && cMsTimeout > 0) + { +# ifdef RT_OS_DARWIN + /** @todo check how Ctrl-C works on Darwin. + * Update: It doesn't work. MACH_RCV_INTERRUPT could perhaps be returned + * to __CFRunLoopServiceMachPort, but neither it nor __CFRunLoopRun + * has any way of expressing it via their return values. So, if + * Ctrl-C handling is important, signal needs to be handled on + * a different thread or something. */ + vrc = waitForEventsOnDarwin(cMsTimeout); +# else // !RT_OS_DARWIN + vrc = waitForEventsOnXPCOM(mEventQ, cMsTimeout); +# endif // !RT_OS_DARWIN + if ( RT_SUCCESS(vrc) + || vrc == VERR_TIMEOUT) + { + int vrc2 = processPendingEvents(mEventQ); + /* If the wait was successful don't fail the whole operation. */ + if (RT_FAILURE(vrc) && RT_FAILURE(vrc2)) + vrc = vrc2; + } + } + + if ( ( RT_SUCCESS(vrc) + || vrc == VERR_INTERRUPTED + || vrc == VERR_TIMEOUT) + && mInterrupted) + { + mInterrupted = false; + vrc = VERR_INTERRUPTED; + } + +#else // !VBOX_WITH_XPCOM + if (cMsTimeout == RT_INDEFINITE_WAIT) + { + BOOL fRet = 0; /* Shut up MSC */ + MSG Msg; + vrc = VINF_SUCCESS; + while ( vrc != VERR_INTERRUPTED + && (fRet = GetMessage(&Msg, NULL /*hWnd*/, WM_USER, WM_USER)) + && fRet != -1) + vrc = NativeEventQueue::dispatchMessageOnWindows(&Msg, vrc); + if (fRet == 0) + vrc = VERR_INTERRUPTED; + else if (fRet == -1) + vrc = RTErrConvertFromWin32(GetLastError()); + } + else + { + vrc = processPendingEvents(); + if ( vrc == VERR_TIMEOUT + && cMsTimeout != 0) + { + DWORD rcW = MsgWaitForMultipleObjects(1, + &mhThread, + TRUE /*fWaitAll*/, + cMsTimeout, + QS_ALLINPUT); + AssertMsgReturn(rcW == WAIT_TIMEOUT || rcW == WAIT_OBJECT_0, + ("%d\n", rcW), + VERR_INTERNAL_ERROR_4); + vrc = processPendingEvents(); + } + } +#endif // !VBOX_WITH_XPCOM + + Assert(vrc != VERR_TIMEOUT || cMsTimeout != RT_INDEFINITE_WAIT); + return vrc; +} + +/** + * Interrupt thread waiting on event queue processing. + * + * Can be called on any thread. + * + * @returns VBox status code. + */ +int NativeEventQueue::interruptEventQueueProcessing() +{ + /* Send a NULL event. This event will be picked up and handled specially + * both for XPCOM and Windows. It is the responsibility of the caller to + * take care of not running the loop again in a way which will hang. */ + postEvent(NULL); + return VINF_SUCCESS; +} + +/** + * Posts an event to this event loop asynchronously. + * + * @param pEvent the event to post, must be allocated using |new| + * @return @c TRUE if successful and false otherwise + */ +BOOL NativeEventQueue::postEvent(NativeEvent *pEvent) +{ +#ifndef VBOX_WITH_XPCOM + /* Note! The event == NULL case is duplicated in vboxapi/PlatformMSCOM::interruptWaitEvents(). */ + BOOL fRc = PostThreadMessage(mThreadId, WM_USER, (WPARAM)pEvent, EVENTQUEUE_WIN_LPARAM_MAGIC); + if (!fRc) + { + static int s_cBitchedAboutFullNativeEventQueue = 0; + if ( GetLastError() == ERROR_NOT_ENOUGH_QUOTA + && s_cBitchedAboutFullNativeEventQueue < 10) + LogRel(("Warning: Asynchronous event queue (%p, thread %RI32) full, event (%p) not delivered (%d/10)\n", + this, mThreadId, pEvent, ++s_cBitchedAboutFullNativeEventQueue)); + else + AssertFailed(); + } + return fRc; +#else // VBOX_WITH_XPCOM + if (!mEventQ) + return FALSE; + + try + { + MyPLEvent *pMyEvent = new MyPLEvent(pEvent); + mEventQ->InitEvent(pMyEvent, this, com::NativeEventQueue::plEventHandler, + com::NativeEventQueue::plEventDestructor); + HRESULT hrc = mEventQ->PostEvent(pMyEvent); + return NS_SUCCEEDED(hrc); + } + catch (std::bad_alloc &ba) + { + AssertMsgFailed(("Out of memory while allocating memory for event=%p: %s\n", + pEvent, ba.what())); + } + + return FALSE; +#endif // VBOX_WITH_XPCOM +} + +/** + * Get select()'able selector for this event queue. + * This will return -1 on platforms and queue variants not supporting such + * functionality. + */ +int NativeEventQueue::getSelectFD() +{ +#ifdef VBOX_WITH_XPCOM + return mEventQ->GetEventQueueSelectFD(); +#else + return -1; +#endif +} + +} +/* namespace com */ diff --git a/src/VBox/Main/glue/VBoxLogRelCreate.cpp b/src/VBox/Main/glue/VBoxLogRelCreate.cpp new file mode 100644 index 00000000..98b72427 --- /dev/null +++ b/src/VBox/Main/glue/VBoxLogRelCreate.cpp @@ -0,0 +1,214 @@ +/* $Id: VBoxLogRelCreate.cpp $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - VBoxLogRelCreate. + */ + +/* + * Copyright (C) 2005-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "package-generated.h" + + + +namespace com +{ + +static const char *g_pszLogEntity = NULL; + +static DECLCALLBACK(void) vboxHeaderFooter(PRTLOGGER pReleaseLogger, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog) +{ + /* some introductory information */ + static RTTIMESPEC s_TimeSpec; + char szTmp[256]; + if (enmPhase == RTLOGPHASE_BEGIN) + RTTimeNow(&s_TimeSpec); + RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp)); + + switch (enmPhase) + { + case RTLOGPHASE_BEGIN: + { + bool fOldBuffered = RTLogSetBuffering(pReleaseLogger, true /*fBuffered*/); + pfnLog(pReleaseLogger, + "VirtualBox %s %s r%u %s (%s %s) release log\n" +#ifdef VBOX_BLEEDING_EDGE + "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n" +#endif + "Log opened %s\n", + g_pszLogEntity, VBOX_VERSION_STRING, RTBldCfgRevision(), + RTBldCfgTargetDotArch(), __DATE__, __TIME__, szTmp); + + pfnLog(pReleaseLogger, "Build Type: %s\n", KBUILD_TYPE); + int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp)); + if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) + pfnLog(pReleaseLogger, "OS Product: %s\n", szTmp); + vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp)); + if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) + pfnLog(pReleaseLogger, "OS Release: %s\n", szTmp); + vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp)); + if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) + pfnLog(pReleaseLogger, "OS Version: %s\n", szTmp); + vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp)); + if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) + pfnLog(pReleaseLogger, "OS Service Pack: %s\n", szTmp); + + vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szTmp, sizeof(szTmp)); + if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) + pfnLog(pReleaseLogger, "DMI Product Name: %s\n", szTmp); + vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION, szTmp, sizeof(szTmp)); + if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) + pfnLog(pReleaseLogger, "DMI Product Version: %s\n", szTmp); + + RTSYSFWTYPE enmType; + vrc = RTSystemQueryFirmwareType(&enmType); + if (RT_SUCCESS(vrc)) + { + pfnLog(pReleaseLogger, "Firmware type: %s\n", RTSystemFirmwareTypeName(enmType)); + if (enmType == RTSYSFWTYPE_UEFI) + { + bool fValue; + vrc = RTSystemQueryFirmwareBoolean(RTSYSFWBOOL_SECURE_BOOT, &fValue); + if (RT_SUCCESS(vrc)) + pfnLog(pReleaseLogger, "Secure Boot: %s\n", fValue ? "Enabled" : "Disabled"); + else + pfnLog(pReleaseLogger, "Secure Boot: %Rrc\n", vrc); + } + } + else + pfnLog(pReleaseLogger, "Firmware type: failed - %Rrc\n", vrc); + + uint64_t cbHostRam = 0, cbHostRamAvail = 0; + vrc = RTSystemQueryTotalRam(&cbHostRam); + if (RT_SUCCESS(vrc)) + vrc = RTSystemQueryAvailableRam(&cbHostRamAvail); + if (RT_SUCCESS(vrc)) + { + pfnLog(pReleaseLogger, "Host RAM: %lluMB", cbHostRam / _1M); + if (cbHostRam > _2G) + pfnLog(pReleaseLogger, " (%lld.%lldGB)", + cbHostRam / _1G, (cbHostRam % _1G) / (_1G / 10)); + pfnLog(pReleaseLogger, " total, %lluMB", cbHostRamAvail / _1M); + if (cbHostRamAvail > _2G) + pfnLog(pReleaseLogger, " (%lld.%lldGB)", + cbHostRamAvail / _1G, (cbHostRamAvail % _1G) / (_1G / 10)); + pfnLog(pReleaseLogger, " available\n"); + } + + /* the package type is interesting for Linux distributions */ + char szExecName[RTPATH_MAX]; + char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName)); + pfnLog(pReleaseLogger, + "Executable: %s\n" + "Process ID: %u\n" + "Package type: %s" +#ifdef VBOX_OSE + " (OSE)" +#endif + "\n", + pszExecName ? pszExecName : "unknown", + RTProcSelf(), + VBOX_PACKAGE_STRING); + RTLogSetBuffering(pReleaseLogger, fOldBuffered); + break; + } + + case RTLOGPHASE_PREROTATE: + pfnLog(pReleaseLogger, "Log rotated - Log started %s\n", szTmp); + break; + + case RTLOGPHASE_POSTROTATE: + pfnLog(pReleaseLogger, "Log continuation - Log started %s\n", szTmp); + break; + + case RTLOGPHASE_END: + pfnLog(pReleaseLogger, "End of log file - Log started %s\n", szTmp); + break; + + default: + /* nothing */; + } +} + +int VBoxLogRelCreate(const char *pcszEntity, const char *pcszLogFile, + uint32_t fFlags, const char *pcszGroupSettings, + const char *pcszEnvVarBase, uint32_t fDestFlags, + uint32_t cMaxEntriesPerGroup, uint32_t cHistory, + uint32_t uHistoryFileTime, uint64_t uHistoryFileSize, + PRTERRINFO pErrInfo) +{ + return VBoxLogRelCreateEx(pcszEntity, pcszLogFile, + fFlags, pcszGroupSettings, + pcszEnvVarBase, fDestFlags, + cMaxEntriesPerGroup, cHistory, + uHistoryFileTime, uHistoryFileSize, + NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/, + pErrInfo); +} + +int VBoxLogRelCreateEx(const char *pcszEntity, const char *pcszLogFile, + uint32_t fFlags, const char *pcszGroupSettings, + const char *pcszEnvVarBase, uint32_t fDestFlags, + uint32_t cMaxEntriesPerGroup, uint32_t cHistory, + uint32_t uHistoryFileTime, uint64_t uHistoryFileSize, + const void *pOutputIf, void *pvOutputIfUser, + PRTERRINFO pErrInfo) +{ + /* create release logger */ + PRTLOGGER pReleaseLogger; + static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + fFlags |= RTLOGFLAGS_USECRLF; +#endif + g_pszLogEntity = pcszEntity; + int vrc = RTLogCreateEx(&pReleaseLogger, pcszEnvVarBase, fFlags, pcszGroupSettings, RT_ELEMENTS(s_apszGroups), s_apszGroups, + cMaxEntriesPerGroup, 0 /*cBufDescs*/, NULL /*paBufDescs*/, fDestFlags, + vboxHeaderFooter, cHistory, uHistoryFileSize, uHistoryFileTime, + (PCRTLOGOUTPUTIF)pOutputIf, pvOutputIfUser, + pErrInfo, pcszLogFile ? "%s" : NULL, pcszLogFile); + if (RT_SUCCESS(vrc)) + { + /* explicitly flush the log, to have some info when buffering */ + RTLogFlush(pReleaseLogger); + + /* register this logger as the release logger */ + RTLogRelSetDefaultInstance(pReleaseLogger); + } + return vrc; +} + +} /* namespace com */ diff --git a/src/VBox/Main/glue/com.cpp b/src/VBox/Main/glue/com.cpp new file mode 100644 index 00000000..25f0a49f --- /dev/null +++ b/src/VBox/Main/glue/com.cpp @@ -0,0 +1,185 @@ +/* $Id: com.cpp $ */ +/** @file + * MS COM / XPCOM Abstraction Layer + */ + +/* + * Copyright (C) 2005-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN +#if !defined(VBOX_WITH_XPCOM) + +# include + +#else /* !defined (VBOX_WITH_XPCOM) */ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# define IPC_DCONNECTSERVICE_CONTRACTID "@mozilla.org/ipc/dconnect-service;1" // official XPCOM headers don't define it yet +#endif /* !defined (VBOX_WITH_XPCOM) */ + +#include "VBox/com/com.h" +#include "VBox/com/assert.h" + +#include "VBox/com/Guid.h" +#include "VBox/com/array.h" + +#include + +#include +#include + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +namespace com +{ +/* static */ +const Guid Guid::Empty; /* default ctor is OK */ + +const char Zeroes[16] = {0, }; + + +void GetInterfaceNameByIID(const GUID &aIID, BSTR *aName) +{ + AssertPtrReturnVoid(aName); + *aName = NULL; + +#if !defined(VBOX_WITH_XPCOM) + + LPOLESTR iidStr = NULL; + if (StringFromIID(aIID, &iidStr) == S_OK) + { + HKEY ifaceKey; + LSTATUS lrc = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Interface", 0, KEY_QUERY_VALUE, &ifaceKey); + if (lrc == ERROR_SUCCESS) + { + HKEY iidKey; + lrc = RegOpenKeyExW(ifaceKey, iidStr, 0, KEY_QUERY_VALUE, &iidKey); + if (lrc == ERROR_SUCCESS) + { + /* determine the size and type */ + DWORD sz, type; + lrc = RegQueryValueExW(iidKey, NULL, NULL, &type, NULL, &sz); + if (lrc == ERROR_SUCCESS && type == REG_SZ) + { + /* query the value to BSTR */ + *aName = SysAllocStringLen(NULL, (sz + 1) / sizeof(TCHAR) + 1); + lrc = RegQueryValueExW(iidKey, NULL, NULL, NULL, (LPBYTE) *aName, &sz); + if (lrc != ERROR_SUCCESS) + { + SysFreeString(*aName); + *aName = NULL; + } + } + RegCloseKey(iidKey); + } + RegCloseKey(ifaceKey); + } + CoTaskMemFree(iidStr); + } + +#else /* !defined (VBOX_WITH_XPCOM) */ + + nsresult rv; + nsCOMPtr iim = + do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) + { + nsCOMPtr iinfo; + rv = iim->GetInfoForIID(&aIID, getter_AddRefs(iinfo)); + if (NS_SUCCEEDED(rv)) + { + const char *iname = NULL; + iinfo->GetNameShared(&iname); + char *utf8IName = NULL; + if (RT_SUCCESS(RTStrCurrentCPToUtf8(&utf8IName, iname))) + { + PRTUTF16 utf16IName = NULL; + if (RT_SUCCESS(RTStrToUtf16(utf8IName, &utf16IName))) + { + *aName = SysAllocString((OLECHAR *) utf16IName); + RTUtf16Free(utf16IName); + } + RTStrFree(utf8IName); + } + } + } + +#endif /* !defined (VBOX_WITH_XPCOM) */ +} + +#ifdef VBOX_WITH_XPCOM + +HRESULT GlueCreateObjectOnServer(const CLSID &clsid, + const char *serverName, + const nsIID &id, + void** ppobj) +{ + HRESULT hrc = E_UNEXPECTED; + nsCOMPtr ipcServ = do_GetService(IPC_SERVICE_CONTRACTID, &hrc); + if (SUCCEEDED(hrc)) + { + PRUint32 serverID = 0; + hrc = ipcServ->ResolveClientName(serverName, &serverID); + if (SUCCEEDED (hrc)) + { + nsCOMPtr dconServ = do_GetService(IPC_DCONNECTSERVICE_CONTRACTID, &hrc); + if (SUCCEEDED(hrc)) + hrc = dconServ->CreateInstance(serverID, + clsid, + id, + ppobj); + } + } + return hrc; +} + +HRESULT GlueCreateInstance(const CLSID &clsid, + const nsIID &id, + void** ppobj) +{ + nsCOMPtr manager; + HRESULT hrc = NS_GetComponentManager(getter_AddRefs(manager)); + if (SUCCEEDED(hrc)) + hrc = manager->CreateInstance(clsid, + nsnull, + id, + ppobj); + return hrc; +} + +#endif // VBOX_WITH_XPCOM + +} /* namespace com */ + diff --git a/src/VBox/Main/glue/constants-python.xsl b/src/VBox/Main/glue/constants-python.xsl new file mode 100755 index 00000000..196377f8 --- /dev/null +++ b/src/VBox/Main/glue/constants-python.xsl @@ -0,0 +1,198 @@ + + + + + + + + + + +# -*- coding: utf-8 -*- + +""" +VirtualBox COM/XPCOM constants. + +This file is autogenerated from VirtualBox.xidl, DO NOT EDIT! +""" + +__copyright__ = \ +""" +Copyright (C) 2009-2023 Oracle and/or its affiliates. + +This file is part of VirtualBox base platform packages, as +available from https://www.virtualbox.org. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, in version 3 of the +License. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see <https://www.gnu.org/licenses>. + +SPDX-License-Identifier: GPL-3.0-only +""" + +__version__ = "$Revision: 155255 $"; + + + +class VirtualBoxReflectionInfo: + """ + Enum constants for the various python styles. + """ + + def __init__(self, fIsSym): + self.__fIsSym = fIsSym + + # iprt/err.h + VBox/err.h constants: + __dVBoxStatuses = { + + + + + + ' ': { + + + ' + ': + , + + + }, + + + + + + ' ': { + + + ' ': ' + + ', + + + }, + + + + + + + + + + ' _ + ': + , + + + + + + + ' ': + , + + + + + }, **__dVBoxStatuses) + + __dValuesFlatSym = { + + + + + + + + + + ' ': + ' ', + + + + # Result constants: + + + ' ': + '', + + + } + + def __getattr__(self, sAttrName): + if self.__fIsSym: + oValue = self.__dValuesFlatSym.get(sAttrName) + else: + oValue = self.__dValuesFlat.get(sAttrName) + if oValue is None: + raise AttributeError + return oValue + + def all_values(self, sEnumName): + """ Returns a dictionary with all the value names for a given enum type. """ + if self.__fIsSym: + dValues = self.__dValuesSym.get(sEnumName) + else: + dValues = self.__dValues.get(sEnumName) + if dValues is None: + dValues = {} + return dValues + + + + + diff --git a/src/VBox/Main/glue/errorprint.cpp b/src/VBox/Main/glue/errorprint.cpp new file mode 100644 index 00000000..756823ca --- /dev/null +++ b/src/VBox/Main/glue/errorprint.cpp @@ -0,0 +1,222 @@ +/* $Id: errorprint.cpp $ */ + +/** @file + * MS COM / XPCOM Abstraction Layer: + * Error info print helpers. This implements the shared code from the macros from errorprint.h. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +#include +#include +#include + +#include + +#include +#include +#include + +namespace com +{ + +void GluePrintErrorInfo(const com::ErrorInfo &info) +{ + bool haveResultCode = false; +#if defined (RT_OS_WIN) + haveResultCode = info.isFullAvailable(); + bool haveComponent = true; + bool haveInterfaceID = true; +#else /* defined (RT_OS_WIN) */ + haveResultCode = true; + bool haveComponent = info.isFullAvailable(); + bool haveInterfaceID = info.isFullAvailable(); +#endif + + try + { + HRESULT hrc = S_OK; + Utf8Str str; + RTCList comp; + + Bstr bstrDetailsText = info.getText(); + if (!bstrDetailsText.isEmpty()) + str = Utf8StrFmt("%ls\n", + bstrDetailsText.raw()); + if (haveResultCode) + { + hrc = info.getResultCode(); + comp.append(Utf8StrFmt("code %Rhrc (0x%RX32)", hrc, hrc)); + } + if (haveComponent) + comp.append(Utf8StrFmt("component %ls", + info.getComponent().raw())); + if (haveInterfaceID) + comp.append(Utf8StrFmt("interface %ls", + info.getInterfaceName().raw())); + if (!info.getCalleeName().isEmpty()) + comp.append(Utf8StrFmt("callee %ls", + info.getCalleeName().raw())); + + if (comp.size() > 0) + { + str += "Details: "; + for (size_t i = 0; i < comp.size() - 1; ++i) + str += comp.at(i) + ", "; + str += comp.last(); + str += "\n"; + } + + // print and log + if (FAILED(hrc)) + { + RTMsgError("%s", str.c_str()); + Log(("ERROR: %s", str.c_str())); + } + else + { + RTMsgWarning("%s", str.c_str()); + Log(("WARNING: %s", str.c_str())); + } + } + catch (std::bad_alloc &) + { + RTMsgError("std::bad_alloc in GluePrintErrorInfo!"); + Log(("ERROR: std::bad_alloc in GluePrintErrorInfo!\n")); + } +} + +void GluePrintErrorContext(const char *pcszContext, const char *pcszSourceFile, uint32_t ulLine, bool fWarning /* = false */) +{ + // pcszSourceFile comes from __FILE__ macro, which always contains the full path, + // which we don't want to see printed: + // print and log + const char *pszFilenameOnly = RTPathFilename(pcszSourceFile); + if (!fWarning) + { + RTMsgError("Context: \"%s\" at line %d of file %s\n", pcszContext, ulLine, pszFilenameOnly); + Log(("ERROR: Context: \"%s\" at line %d of file %s\n", pcszContext, ulLine, pszFilenameOnly)); + } + else + { + RTMsgWarning("Context: \"%s\" at line %d of file %s\n", pcszContext, ulLine, pszFilenameOnly); + Log(("WARNING: Context: \"%s\" at line %d of file %s\n", pcszContext, ulLine, pszFilenameOnly)); + } +} + +void GluePrintRCMessage(HRESULT hrc) +{ + // print and log + if (FAILED(hrc)) + { + RTMsgError("Code %Rhra (extended info not available)\n", hrc); + Log(("ERROR: Code %Rhra (extended info not available)\n", hrc)); + } + else + { + RTMsgWarning("Code %Rhra (extended info not available)\n", hrc); + Log(("WARNING: Code %Rhra (extended info not available)\n", hrc)); + } +} + +static void glueHandleComErrorInternal(com::ErrorInfo &info, + const char *pcszContext, + HRESULT hrc, + const char *pcszSourceFile, + uint32_t ulLine) +{ + if (info.isFullAvailable() || info.isBasicAvailable()) + { + const com::ErrorInfo *pInfo = &info; + do + { + GluePrintErrorInfo(*pInfo); + + /* Use hrc for figuring out if there were just warnings. */ + HRESULT hrc2 = pInfo->getResultCode(); + if ( (SUCCEEDED_WARNING(hrc) && FAILED(hrc2)) + || (SUCCEEDED(hrc) && (FAILED(hrc2) || SUCCEEDED_WARNING(hrc2)))) + hrc = hrc2; + + pInfo = pInfo->getNext(); + /* If there is more than one error, separate them visually. */ + if (pInfo) + { + /* If there are several errors then at least basic error + * information must be available, otherwise something went + * horribly wrong. */ + Assert(pInfo->isFullAvailable() || pInfo->isBasicAvailable()); + + RTMsgError("--------\n"); + } + } while (pInfo); + } + else + GluePrintRCMessage(hrc); + + if (pcszContext != NULL || pcszSourceFile != NULL) + GluePrintErrorContext(pcszContext, pcszSourceFile, ulLine, SUCCEEDED_WARNING(hrc)); +} + +void GlueHandleComError(ComPtr iface, + const char *pcszContext, + HRESULT hrc, + const char *pcszSourceFile, + uint32_t ulLine) +{ + /* If we have full error info, print something nice, and start with the + * actual error message. */ + com::ErrorInfo info(iface, COM_IIDOF(IUnknown)); + + glueHandleComErrorInternal(info, + pcszContext, + hrc, + pcszSourceFile, + ulLine); + +} + +void GlueHandleComErrorNoCtx(ComPtr iface, HRESULT hrc) +{ + GlueHandleComError(iface, NULL, hrc, NULL, 0); +} + +void GlueHandleComErrorProgress(ComPtr progress, + const char *pcszContext, + HRESULT hrc, + const char *pcszSourceFile, + uint32_t ulLine) +{ + /* Get the error info out of the progress object. */ + ProgressErrorInfo ei(progress); + + glueHandleComErrorInternal(ei, + pcszContext, + hrc, + pcszSourceFile, + ulLine); +} + +} /* namespace com */ + diff --git a/src/VBox/Main/glue/glue-java.xsl b/src/VBox/Main/glue/glue-java.xsl new file mode 100644 index 00000000..d042312d --- /dev/null +++ b/src/VBox/Main/glue/glue-java.xsl @@ -0,0 +1,5259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /* + * Copyright (C) 2010-2023 Oracle and/or its affiliates. + * + * This file is part of a free software library; you can redistribute + * it and/or modify it under the terms of the GNU Lesser General + * Public License version 2.1 as published by the Free Software + * Foundation and shipped in the \"COPYING.LIB\" file with this library. + * The library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY of any kind. + * + * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if + * any license choice other than GPL or LGPL is available it will + * apply instead, Oracle elects to use only the Lesser General Public + * License version 2.1 (LGPLv2) at this time for any software where + * a choice of LGPL license versions is made available with the + * language indicating that LGPLv2 or any later version may be used, + * or where a choice of which version of the LGPL is applied is + * otherwise unspecified. + + * http://www.virtualbox.org. This library is free software; you can + * redistribute it and/or modify it under the terms of the GNU Lesser General + * Public License as published by the Free Software Foundation, in version 2.1 + * as it comes in the "COPYING.LIB" file of the VirtualBox SDK distribution. + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/* + + + + * + * DO NOT EDIT! This is a generated file. + * Generated from: src/VBox/Main/idl/VirtualBox.xidl (VirtualBox's interface definitions in XML) + * Generator: src/VBox/Main/glue/glue-java.xsl + */ + + + + + + + + + + + + + + + + + + + + + import org.mozilla.interfaces.*; + + + + import com.jacob.com.*; + import com.jacob.activeX.ActiveXComponent; + + + + import javax.xml.ws.*; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <code> + + + <h2> + + + + + + + + + </code> + + + </h2> + + + + + + + + + + + + + + + + + + ( + + + + , + + + + Holder + + + + + + + + + + + + ) + + + + + + () + + + + + + + + + + + + {@link + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + NOTE: + + + + + + + + + + + + + + + + @see + + + + + + + + + + + + + + + + + + + + + + <p></p><dl><dt><b>Expected result codes:</b></dt> + + <dd><code> + + + + + + + + + </code> - + + </dd> + + </dl> + + + + + + + + Interface ID: <code>{ + + + + }</code> */ + + + + + + + + + @return + + + + yes + + + + */ + + + + + + + + + @param value + + + + yes + + + + */ + + + + + + + + + + + + + + */ + + + + + + + + @return + + + @param + + + + + + + + + + + + + + Interface ID: <code>{ + + + + }</code> */ + + + + + + + */ + + + + + + + + + + + + + + + + + + + + + + { + + + + + + + , + + + ; + + + + + + private final int value; + + + { + value = v; + } + + public int value() + { + return value; + } + + + { + + { + if (c.value == (int)v) + { + return c; + } + } + throw new IllegalArgumentException(Long.toString(v)); + } + + + { + + } + } + + + + + + + + + + + + + this.getObjMgr().preventObjRelease(); + + try + { + + + + + + + + } + catch (org.mozilla.xpcom.XPCOMException e) + { + throw new VBoxException(e.getMessage(), e); + } + + + + } + catch (com.jacob.com.ComException e) + { + throw new VBoxException(e.getMessage(), e); + } + + + + } + catch (InvalidObjectFaultMsg e) + { + throw new VBoxException(e.getMessage(), e, this.getObjMgr(), this.port); + } + catch (RuntimeFaultMsg e) + { + throw new VBoxException(e.getMessage(), e, this.getObjMgr(), this.port); + } + + finally + { + getObjMgr().allowObjRelease(); + } + + + + + + + + + + + + + + + + + + + + + com.jacob.com.Dispatch + + + + String + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + List + + + + &lt; + + + < + + + + + + + + + + + + + + + IUnknown + + + + + + + + + + + + + + + &gt; + + + > + + + + + + [] + + + + + + + + + + + + + + + + + long + + + + long + + + + int + + + + int + + + + short + + + + byte + + + + boolean + + + + nsISupports + + + + String + + + + String + + + + + + + + + + + + + + + + long + + + + + + + + + + + [] + + + + + Variant + + + + + + + List< + + + + String + + + + String + + + + + + + + + + + + + /*base64*/String + + + + Long + + + + Long + + + + Integer + + + + Integer + + + + Short + + + + Boolean + + + + String + + + + String + + + + + + + + + + + + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + null + + + + + null, + + + + + + + + + + + + + + + + + + + + + + + + , + + + ); + + + + + + + + + + , + + + + + + + + + + + + + + + + + ); + + + + + + + + + + + + obj + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + , + + + + + ); + + + + + + + + + + + + + + + + + + + + + + null + + ); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + private VboxPortType port; + private ObjectRefManager objMgr; + + + + + + + + + + + + + + + + + + + + + { + this.real = real; + this.port = port; + this.objMgr = objMgr; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + + + + + + + + + + + + + + + + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + void + + + + + + + retVal.value + + + retVal + + + + + + + + + + + + + + + + + + + + + + + , + + + ) + { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + void + + + + + + + + + + + + + + + + + + + + + + , + + + ); + + + + + + + + + + + { + + + + + + + + nsISupports nsobj = obj != null ? (nsISupports)obj.getWrapped() : null; + if (nsobj == null) return null; + + + + + + + + + + + + + + + + + + + + + } + + + + + + + + + + + + + + + + + + + + + + + + + void + + + + + + + + + + + + + + + + + + + + + + + + + + + , + + + ) + { + + + + + + + + + + Variant _args[]) + { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + , + + + ); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + + + + + + + + + + + + + + + + + + + { + super(wrapped, objMgr, port); + } + + + + + { + super(wrapped); + } + + + + { + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + + + + + + + + + + + + + + + + + + + + + } + + + + + + + + + + + + + + { + + + + + + + + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + import java.util.List; + + + + + + + { + + + + + + + + + + + { + + + + { + + + + + + + + + + + + + + + } + + + + + + + + + + + + + + + + + + + import java.util.List; + + + { + + + + + + + + + + } + + + + + + + + + + + import java.util.List; + + + + + + + + + + + + { + + + + + { + + + + + + + { + this.sink = sink; + } + + + + + + + + + + } + + + + + + + + + + + + + + + +{ + public T value; + + public Holder() + { + } + public Holder(T value) + { + this.value = value; + } +} +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wrap(byte[] values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.length); + for (short v : values) + { + ret.add(v); + } + return ret; + } + + public static List wrap(short[] values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.length); + for (short v : values) + { + ret.add(v); + } + return ret; + } + + public static List wrap(int[] values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.length); + for (int v : values) + { + ret.add(v); + } + return ret; + } + + public static List wrap(long[] values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.length); + for (long v : values) + { + ret.add(v); + } + return ret; + } + + public static List wrap(boolean[] values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.length); + for (boolean v: values) + { + ret.add(v); + } + return ret; + } + + public static List wrap(String[] values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.length); + for (String v : values) + { + ret.add(v); + } + return ret; + } + + public static List wrap(Class wrapperClass, T[] values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.length); + for (T v : values) + { + ret.add(v); + } + return ret; + } + + @SuppressWarnings( "unchecked") + public static List wrapEnum(Class wrapperClass, long values[]) + { + try + { + if (values == null) + return null; + //// This code is questionable, as it invokes a private constructor + //// (all enums only have default constructors), and we don't really + //// know what to pass as the name, and the ordinal may or may not + //// be sensible, especially if the long was abused as a bitset. + //Constructor c = wrapperClass.getDeclaredConstructor(String.class, int.class, int.class); + //c.setAccessible(true); // make it callable + //List ret = new ArrayList(values.length); + //for (long v : values) + //{ + // T convEnum = c.newInstance("unknown", (int)v, (int)v); + // ret.add(convEnum); + //} + + // Alternative implementation: use the fromValue method, which is + // what the code handling single enums will do. I see no reason to + // use the above very ugly hack if there are better alternatives, + // which as a bonus complain about unknown values. This variant is + // slower, but also orders of magnitude safer. + java.lang.reflect.Method fromValue = wrapperClass.getMethod("fromValue", long.class); + List ret = new ArrayList(values.length); + for (long v : values) + { + T convEnum = (T)fromValue.invoke(null, v); + ret.add(convEnum); + } + return ret; + } + catch (NoSuchMethodException e) + { + throw new AssertionError(e); + } + //catch (InstantiationException e) + //{ + // throw new AssertionError(e); + //} + catch (IllegalAccessException e) + { + throw new AssertionError(e); + } + catch (InvocationTargetException e) + { + throw new AssertionError(e); + } + } + public static short[] unwrapUShort(List values) + { + if (values == null) + return null; + + short[] ret = new short[values.size()]; + int i = 0; + for (short l : values) + { + ret[i++] = l; + } + return ret; + } + + public static int[] unwrapInteger(List values) + { + if (values == null) + return null; + + int[] ret = new int[values.size()]; + int i = 0; + for (int l : values) + { + ret[i++] = l; + } + return ret; + } + + public static long[] unwrapULong(List values) + { + if (values == null) + return null; + + long[] ret = new long[values.size()]; + int i = 0; + for (long l : values) + { + ret[i++] = l; + } + return ret; + } + + public static boolean[] unwrapBoolean(List values) + { + if (values == null) + return null; + + boolean[] ret = new boolean[values.size()]; + int i = 0; + for (boolean l : values) + { + ret[i++] = l; + } + return ret; + } + + public static String[] unwrapStr(List values) + { + if (values == null) + return null; + + String[] ret = new String[values.size()]; + int i = 0; + for (String l : values) + { + ret[i++] = l; + } + return ret; + } + + public static > long[] unwrapEnum(Class enumClass, List values) + { + if (values == null) + return null; + + long result[] = new long[values.size()]; + try + { + java.lang.reflect.Method valueM = enumClass.getMethod("value"); + int i = 0; + for (T v : values) + { + result[i++] = (Integer)valueM.invoke(v); + } + return result; + } + catch (NoSuchMethodException e) + { + throw new AssertionError(e); + } + catch(SecurityException e) + { + throw new AssertionError(e); + } + catch (IllegalAccessException e) + { + throw new AssertionError(e); + } + catch (IllegalArgumentException e) + { + throw new AssertionError(e); + } + catch (InvocationTargetException e) + { + throw new AssertionError(e); + } + } + + public static List wrap2(Class wrapperClass1, Class wrapperClass2, T2[] values) + { + try + { + if (values == null) + return null; + + Constructor c = wrapperClass1.getConstructor(wrapperClass2); + List ret = new ArrayList(values.length); + for (T2 v : values) + { + ret.add(c.newInstance(v)); + } + return ret; + } + catch (NoSuchMethodException e) + { + throw new AssertionError(e); + } + catch (InstantiationException e) + { + throw new AssertionError(e); + } + catch (IllegalAccessException e) + { + throw new AssertionError(e); + } + catch (InvocationTargetException e) + { + throw new AssertionError(e); + } + } + + @SuppressWarnings( "unchecked") + public static T[] unwrap(Class wrapperClass, List values) + { + if (values == null) + return null; + if (values.size() == 0) + return null; + return (T[])values.toArray((T[])Array.newInstance(wrapperClass, values.size())); + } + + @SuppressWarnings( "unchecked" ) + public static T queryInterface(Object obj, String uuid, Class iface) + { + return (T)queryInterface(obj, uuid); + } + + public static Object queryInterface(Object obj, String uuid) + { + try + { + /* Kind of ugly, but does the job of casting */ + org.mozilla.xpcom.Mozilla moz = org.mozilla.xpcom.Mozilla.getInstance(); + long xpobj = moz.wrapJavaObject(obj, uuid); + return moz.wrapXPCOMObject(xpobj, uuid); + } + catch (Exception e) + { + return null; + } + } + + @SuppressWarnings("unchecked") + public static T2[] unwrap2(Class wrapperClass1, Class wrapperClass2, List values) + { + if (values == null) + return null; + + T2 ret[] = (T2[])Array.newInstance(wrapperClass2, values.size()); + int i = 0; + for (T1 obj : values) + { + ret[i++] = (T2)obj.getWrapped(); + } + return ret; + } +} +]]> + + + + + + + + + + + + + +import org.mozilla.xpcom.*; + +public class VBoxException extends RuntimeException +{ + private int resultCode; + private IVirtualBoxErrorInfo errorInfo; + + public VBoxException(String message) + { + super(message); + resultCode = -1; + errorInfo = null; + } + + public VBoxException(String message, Throwable cause) + { + super(message, cause); + if (cause instanceof org.mozilla.xpcom.XPCOMException) + { + resultCode = (int)((org.mozilla.xpcom.XPCOMException)cause).errorcode; + try + { + Mozilla mozilla = Mozilla.getInstance(); + nsIServiceManager sm = mozilla.getServiceManager(); + nsIExceptionService es = (nsIExceptionService)sm.getServiceByContractID("@mozilla.org/exceptionservice;1", nsIExceptionService.NS_IEXCEPTIONSERVICE_IID); + nsIExceptionManager em = es.getCurrentExceptionManager(); + nsIException ex = em.getCurrentException(); + errorInfo = new IVirtualBoxErrorInfo((org.mozilla.interfaces.IVirtualBoxErrorInfo)ex.queryInterface(org.mozilla.interfaces.IVirtualBoxErrorInfo.IVIRTUALBOXERRORINFO_IID)); + } + catch (NullPointerException e) + { + e.printStackTrace(); + // nothing we can do + errorInfo = null; + } + } + else + resultCode = -1; + } + + public int getResultCode() + { + return resultCode; + } + + public IVirtualBoxErrorInfo getVirtualBoxErrorInfo() + { + return errorInfo; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wrap(short[] values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.length); + for (short v : values) + { + ret.add(v); + } + return ret; + } + + public static List wrap(int[] values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.length); + for (int v : values) + { + ret.add(v); + } + return ret; + } + + public static List wrap(long[] values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.length); + for (long v : values) + { + ret.add(v); + } + return ret; + } + + public static List wrap(String[] values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.length); + for (String v : values) + { + ret.add(v); + } + return ret; + } + + public static T wrapDispatch(Class wrapperClass, Dispatch d) + { + try + { + if (d == null || d.m_pDispatch == 0) + return null; + Constructor c = wrapperClass.getConstructor(Dispatch.class); + return (T)c.newInstance(d); + } + catch (NoSuchMethodException e) + { + throw new AssertionError(e); + } + catch (InstantiationException e) + { + throw new AssertionError(e); + } + catch (IllegalAccessException e) + { + throw new AssertionError(e); + } + catch (InvocationTargetException e) + { + throw new AssertionError(e); + } + } + + @SuppressWarnings("unchecked") + public static Object wrapVariant(Class wrapperClass, Variant v) + { + if (v == null) + return null; + + short vt = v.getvt(); + switch (vt) + { + case Variant.VariantNull: + return null; + case Variant.VariantBoolean: + return v.getBoolean(); + case Variant.VariantByte: + return v.getByte(); + case Variant.VariantShort: + return v.getShort(); + case Variant.VariantInt: + return v.getInt(); + case Variant.VariantLongInt: + return v.getLong(); + case Variant.VariantString: + return v.getString(); + case Variant.VariantDispatch: + return wrapDispatch(wrapperClass, v.getDispatch()); + default: + throw new IllegalArgumentException("unhandled variant type " + vt); + } + } + + public static byte[] wrapBytes(SafeArray sa) + { + if (sa == null) + return null; + + int saLen = sa.getUBound() - sa.getLBound() + 1; + + byte[] ret = new byte[saLen]; + int j = 0; + for (int i = sa.getLBound(); i <= sa.getUBound(); i++) + { + Variant v = sa.getVariant(i); + // come up with more effective approach!!! + ret[j++] = v.getByte(); + } + return ret; + } + + @SuppressWarnings("unchecked") + public static List wrap(Class wrapperClass, SafeArray sa) + { + if (sa == null) + return null; + + int saLen = sa.getUBound() - sa.getLBound() + 1; + if (saLen == 0) + return Collections.emptyList(); + + List ret = new ArrayList(saLen); + for (int i = sa.getLBound(); i <= sa.getUBound(); i++) + { + Variant v = sa.getVariant(i); + ret.add((T)wrapVariant(wrapperClass, v)); + } + return ret; + } + + public static List wrapEnum(Class wrapperClass, SafeArray sa) + { + try + { + if (sa == null) + return null; + + int saLen = sa.getUBound() - sa.getLBound() + 1; + if (saLen == 0) + return Collections.emptyList(); + List ret = new ArrayList(saLen); + Constructor c = wrapperClass.getConstructor(int.class); + for (int i = sa.getLBound(); i <= sa.getUBound(); i++) + { + Variant v = sa.getVariant(i); + ret.add(c.newInstance(v.getInt())); + } + return ret; + } + catch (NoSuchMethodException e) + { + throw new AssertionError(e); + } + catch (InstantiationException e) + { + throw new AssertionError(e); + } + catch (IllegalAccessException e) + { + throw new AssertionError(e); + } + catch (InvocationTargetException e) + { + throw new AssertionError(e); + } + } + + public static SafeArray unwrapInt(List values) + { + if (values == null) + return null; + SafeArray ret = new SafeArray(Variant.VariantInt, values.size()); + int i = 0; + for (int l : values) + { + ret.setInt(i++, l); + } + return ret; + } + + public static SafeArray unwrapLong(List values) + { + if (values == null) + return null; + SafeArray ret = new SafeArray(Variant.VariantLongInt, values.size()); + int i = 0; + for (long l : values) + { + ret.setLong(i++, l); + } + return ret; + } + + public static SafeArray unwrapBool(List values) + { + if (values == null) + return null; + + SafeArray result = new SafeArray(Variant.VariantBoolean, values.size()); + int i = 0; + for (boolean l : values) + { + result.setBoolean(i++, l); + } + return result; + } + + + public static SafeArray unwrapBytes(byte[] values) + { + if (values == null) + return null; + + SafeArray result = new SafeArray(Variant.VariantByte, values.length); + int i = 0; + for (byte l : values) + { + result.setByte(i++, l); + } + return result; + } + + + public static > SafeArray unwrapEnum(Class enumClass, List values) + { + if (values == null) + return null; + + SafeArray result = new SafeArray(Variant.VariantInt, values.size()); + try + { + java.lang.reflect.Method valueM = enumClass.getMethod("value"); + int i = 0; + for (T v : values) + { + result.setInt(i++, (Integer)valueM.invoke(v)); + } + return result; + } + catch (NoSuchMethodException e) + { + throw new AssertionError(e); + } + catch(SecurityException e) + { + throw new AssertionError(e); + } + catch (IllegalAccessException e) + { + throw new AssertionError(e); + } + catch (IllegalArgumentException e) + { + throw new AssertionError(e); + } + catch (InvocationTargetException e) + { + throw new AssertionError(e); + } + } + public static SafeArray unwrapString(List values) + { + if (values == null) + return null; + SafeArray result = new SafeArray(Variant.VariantString, values.size()); + int i = 0; + for (String l : values) + { + result.setString(i++, l); + } + return result; + } + + public static List wrap2(Class wrapperClass1, Class wrapperClass2, T2[] values) + { + try + { + if (values == null) + return null; + if (values.length == 0) + return Collections.emptyList(); + + Constructor c = wrapperClass1.getConstructor(wrapperClass2); + List ret = new ArrayList(values.length); + for (T2 v : values) + { + ret.add(c.newInstance(v)); + } + return ret; + } + catch (NoSuchMethodException e) + { + throw new AssertionError(e); + } + catch (InstantiationException e) + { + throw new AssertionError(e); + } + catch (IllegalAccessException e) + { + throw new AssertionError(e); + } + catch (InvocationTargetException e) + { + throw new AssertionError(e); + } + } + + @SuppressWarnings("unchecked") + public static T[] unwrap(Class wrapperClass, List values) + { + if (values == null) + return null; + return (T[])values.toArray((T[])Array.newInstance(wrapperClass, values.size())); + } + + @SuppressWarnings("unchecked") + public static T2[] unwrap2(Class wrapperClass1, Class wrapperClass2, List values) + { + if (values == null) + return null; + + T2 ret[] = (T2[])Array.newInstance(wrapperClass2, values.size()); + int i = 0; + for (T1 obj : values) + { + ret[i++] = (T2)obj.getWrapped(); + } + return ret; + } + + /* We have very long invoke lists sometimes */ + public static Variant invoke(Dispatch d, String method, Object ... args) + { + return Dispatch.callN(d, method, args); + } +} +]]> + + + + + + + + + + + + + + +public class VBoxException extends RuntimeException +{ + private int resultCode; + private IVirtualBoxErrorInfo errorInfo; + + public VBoxException(String message) + { + super(message); + resultCode = -1; + errorInfo = null; + } + + public VBoxException(String message, Throwable cause) + { + super(message, cause); + if (cause instanceof com.jacob.com.ComException) + { + resultCode = ((com.jacob.com.ComException)cause).getHResult(); + // JACOB doesn't support calling GetErrorInfo, which + // means there is no way of getting an IErrorInfo reference, + // and that means no way of getting to IVirtualBoxErrorInfo. + errorInfo = null; + } + else + resultCode = -1; + } + + public int getResultCode() + { + return resultCode; + } + + public IVirtualBoxErrorInfo getVirtualBoxErrorInfo() + { + return errorInfo; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + List wrap(Class wrapperClass, ObjectRefManager objMgr, VboxPortType pt, List values) + { + try + { + if (values == null) + return null; + + Constructor c = wrapperClass.getConstructor(String.class, ObjectRefManager.class, VboxPortType.class); + List ret = new ArrayList(values.size()); + for (String v : values) + { + ret.add(c.newInstance(v, objMgr, pt)); + } + return ret; + } + catch (NoSuchMethodException e) + { + throw new AssertionError(e); + } + catch (InstantiationException e) + { + throw new AssertionError(e); + } + catch (IllegalAccessException e) + { + throw new AssertionError(e); + } + catch (InvocationTargetException e) + { + throw new AssertionError(e); + } + } + + public static List wrap2(Class wrapperClass1, Class wrapperClass2, ObjectRefManager objMgr, VboxPortType pt, List values) + { + try + { + if (values == null) + return null; + + Constructor c = wrapperClass1.getConstructor(wrapperClass2, ObjectRefManager.class, VboxPortType.class); + List ret = new ArrayList(values.size()); + for (T2 v : values) + { + ret.add(c.newInstance(v, objMgr, pt)); + } + return ret; + } + catch (NoSuchMethodException e) + { + throw new AssertionError(e); + } + catch (InstantiationException e) + { + throw new AssertionError(e); + } + catch (IllegalAccessException e) + { + throw new AssertionError(e); + } + catch (InvocationTargetException e) + { + throw new AssertionError(e); + } + } + + public static List unwrap(List values) + { + if (values == null) + return null; + + List ret = new ArrayList(values.size()); + for (T obj : values) + { + ret.add(obj.getWrapped()); + } + return ret; + } + + @SuppressWarnings("unchecked" ) + public static , T2 extends Enum > List convertEnums(Class fromClass, + Class toClass, + List values) + { + try + { + if (values == null) + return null; + List ret = new ArrayList(values.size()); + for (T1 v : values) + { + // Ordinal based enum conversion, as JAX-WS "invents" its own + // enum names and has string values with the expected content. + int enumOrdinal = v.ordinal(); + T2 convEnum = toClass.getEnumConstants()[enumOrdinal]; + ret.add(convEnum); + } + return ret; + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new AssertionError(e); + } + } + + /* Pretty naive Base64 encoder/decoder. */ + private static final char[] valToChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); + private static final int[] charToVal = new int[256]; + + /* Initialize recoding alphabet. */ + static + { + for (int i = 0; i < charToVal.length; i++) + charToVal[i] = -1; + + for (int i = 0; i < valToChar.length; i++) + charToVal[valToChar[i]] = i; + + charToVal['='] = 0; + } + + public static String encodeBase64(byte[] data) + { + if (data == null) + return null; + + if (data.length == 0) + return ""; + + int fullTriplets = data.length / 3; + int resultLen = ((data.length - 1) / 3 + 1) * 4; + char[] result = new char[resultLen]; + int dataIndex = 0, stringIndex = 0; + + for (int i = 0; i < fullTriplets; i++) + { + int ch1 = data[dataIndex++] & 0xff; + result[stringIndex++] = valToChar[ch1 >> 2]; + int ch2 = data[dataIndex++] & 0xff; + result[stringIndex++] = valToChar[((ch1 << 4) & 0x3f) | (ch2 >> 4)]; + int ch3 = data[dataIndex++] & 0xff; + result[stringIndex++] = valToChar[((ch2 << 2) & 0x3f) | (ch3 >> 6)]; + result[stringIndex++] = valToChar[ch3 & 0x3f]; + } + + switch (data.length - dataIndex) + { + case 0: + // do nothing + break; + case 1: + { + int ch1 = data[dataIndex++] & 0xff; + result[stringIndex++] = valToChar[ch1 >> 2]; + result[stringIndex++] = valToChar[(ch1 << 4) & 0x3f]; + result[stringIndex++] = '='; + result[stringIndex++] = '='; + break; + } + case 2: + { + int ch1 = data[dataIndex++] & 0xff; + result[stringIndex++] = valToChar[ch1 >> 2]; + int ch2 = data[dataIndex++] & 0xff; + result[stringIndex++] = valToChar[((ch1 << 4) & 0x3f) | (ch2 >> 4)]; + result[stringIndex++] = valToChar[(ch2 << 2) & 0x3f]; + result[stringIndex++] = '='; + break; + } + default: + throw new VBoxException("bug!"); + } + + return new String(result); + } + + private static int skipInvalid(String str, int stringIndex) + { + while (charToVal[str.charAt(stringIndex)] < 0) + stringIndex++; + + return stringIndex; + } + + public static byte[] decodeBase64(String str) + { + if (str == null) + return null; + + int stringLength = str.length(); + if (stringLength == 0) + return new byte[0]; + + int validChars = 0, padChars = 0; + for (int i = 0; i < str.length(); i++) + { + char ch = str.charAt(i); + + if (charToVal[ch] >= 0) + validChars++; + + if (ch == '=') + padChars++; + } + + if ((validChars * 3 % 4) != 0) + throw new VBoxException("invalid base64 encoded string " + str); + + int resultLength = validChars * 3 / 4 - padChars; + byte[] result = new byte[resultLength]; + + int dataIndex = 0, stringIndex = 0; + int quadraplets = validChars / 4; + + for (int i = 0; i < quadraplets; i++) + { + stringIndex = skipInvalid(str, stringIndex); + int ch1 = str.charAt(stringIndex++); + stringIndex = skipInvalid(str, stringIndex); + int ch2 = str.charAt(stringIndex++); + stringIndex = skipInvalid(str, stringIndex); + int ch3 = str.charAt(stringIndex++); + stringIndex = skipInvalid(str, stringIndex); + int ch4 = str.charAt(stringIndex++); + + result[dataIndex++] = (byte)(((charToVal[ch1] << 2) | charToVal[ch2] >> 4) & 0xff); + /* we check this to ensure that we don't override data with '=' padding. */ + if (dataIndex < result.length) + result[dataIndex++] = (byte)(((charToVal[ch2] << 4) | charToVal[ch3] >> 2) & 0xff); + if (dataIndex < result.length) + result[dataIndex++] = (byte)(((charToVal[ch3] << 6) | charToVal[ch4]) & 0xff); + } + + return result; + } +} +]]> + + + + + + + + + + + + + +public class VBoxException extends RuntimeException +{ + private int resultCode; + private IVirtualBoxErrorInfo errorInfo; + + public VBoxException(String message) + { + super(message); + resultCode = -1; + errorInfo = null; + } + + public VBoxException(String message, Throwable cause) + { + super(message, cause); + resultCode = -1; + errorInfo = null; + } + + public VBoxException(String message, Throwable cause, ObjectRefManager objMgr, VboxPortType port) + { + super(message, cause); + if (cause instanceof RuntimeFaultMsg) + { + RuntimeFaultMsg m = (RuntimeFaultMsg)cause; + RuntimeFault f = m.getFaultInfo(); + resultCode = f.getResultCode(); + String retVal = f.getReturnval(); + errorInfo = (retVal.length() > 0) ? new IVirtualBoxErrorInfo(retVal, objMgr, port) : null; + } + else + resultCode = -1; + } + + public int getResultCode() + { + return resultCode; + } + + public IVirtualBoxErrorInfo getVirtualBoxErrorInfo() + { + return errorInfo; + } +} + + + + + + + + + + + + + + import java.net.URL; +import java.math.BigInteger; +import java.util.Iterator; +import java.util.List; +import java.util.LinkedList; +import java.util.Map; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.lang.Integer; +import java.lang.ref.WeakReference; +import java.lang.ref.ReferenceQueue; +import javax.xml.namespace.QName; +import javax.xml.ws.BindingProvider; +import javax.xml.ws.Holder; +import javax.xml.ws.WebServiceException; +import java.io.IOException; +import java.net.UnknownHostException; +import java.net.Socket; +import java.net.InetAddress; +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.SSLSocket; + +class PortPool +{ + private final static String wsdlFile = + + known; + private boolean initStarted; + private VboxService svc; + + PortPool(boolean usePreinit) + { + known = new HashMap(); + + if (usePreinit) + { + new Thread(new Runnable() + { + public void run() + { + // need to sync on something else but 'this' + synchronized (known) + { + initStarted = true; + known.notify(); + } + + preinit(); + } + }).start(); + + synchronized (known) + { + while (!initStarted) + { + try + { + known.wait(); + } + catch (InterruptedException e) + { + break; + } + } + } + } + } + + private synchronized void preinit() + { + VboxPortType port = getPort(); + releasePort(port); + } + + synchronized VboxPortType getPort() + { + VboxPortType port = null; + int ttl = 0; + + for (VboxPortType cur: known.keySet()) + { + int value = known.get(cur); + if ((value & 0x10000) == 0) + { + port = cur; + ttl = value & 0xffff; + break; + } + } + + if (port == null) + { + if (svc == null) + { + URL wsdl = PortPool.class.getClassLoader().getResource(wsdlFile); + if (wsdl == null) + throw new LinkageError(wsdlFile + " not found, but it should have been in the jar"); + svc = new VboxService(wsdl, + new QName("http://www.virtualbox.org/Service", + "vboxService")); + } + port = svc.getVboxServicePort(); + // reuse this object 0x10 times + ttl = 0x10; + } + // mark as used + known.put(port, new Integer(0x10000 | ttl)); + return port; + } + + synchronized void releasePort(VboxPortType port) + { + Integer val = known.get(port); + if (val == null || val == 0) + { + // know you not + return; + } + + int v = val; + int ttl = v & 0xffff; + // decrement TTL, and throw away port if used too much times + if (--ttl <= 0) + { + known.remove(port); + } + else + { + v = ttl; // set new TTL and clear busy bit + known.put(port, v); + } + } +} + + +/** + * This class manages the object references between us and the webservice server. + * It makes sure that the object on the server side is destroyed when all + */ +class ObjectRefManager +{ + private final static ReferenceQueue refQ = new ReferenceQueue(); + + private final ConcurrentMap map = new ConcurrentHashMap(); + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + private final ObjRefMgrCleanupThread objRefMgrCleanup; + + public ObjectRefManager() + { + this.objRefMgrCleanup = new ObjRefMgrCleanupThread(this, 100); + this.objRefMgrCleanup.start(); + } + + /** + * Prevents the object reference manager cleanup thread from releasing any + * server side objects to avoid a fundamental race in the multi threaded + * java environment where it is possible that a wrapper got the object ID + * from the server but couldn't create the local stub protecting the object + * before the cleanup thread released it. + */ + public void preventObjRelease() + { + lock.readLock().lock(); + } + + /** + * Allows releasing server side objects from the cleanup thread again. + */ + public void allowObjRelease() + { + lock.readLock().unlock(); + } + + /** + * Marks the start of a run to release server side objects which don't hold + * a reference locally anymore. + */ + public void startObjRelease() + { + lock.writeLock().lock(); + } + + /** + * Marks the end of a cleanup run. + */ + public void endObjRelease() + { + lock.writeLock().unlock(); + } + + /** + * Registers a new stub object for automatic reference managing. + */ + public void registerObj(IUnknown obj) + { + assert lock.getReadLockCount() > 0; + ManagedObjRef ref = new ManagedObjRef(obj); + + ManagedObj mgrobj = map.get(obj.getWrapped()); + if (mgrobj != null) + { + mgrobj.addObject(ref); + } + else + { + /* Create new. */ + mgrobj = new ManagedObj(obj.getWrapped(), obj.getRemoteWSPort()); + mgrobj.addObject(ref); + map.put(obj.getWrapped(), mgrobj); + } + } + + /** + * Removes a garbage collected object reference from our reference manager. + * + * Returns the server side object wrapper if there is no stub referencing it + * anymore otherwise null is returned. + */ + public ManagedObj unregisterObj(ManagedObjRef objRef) + { + ManagedObj obj = this.map.get(objRef.objId); + + assert obj != null; + obj.removeObject(objRef); + if (!obj.isReferenced()) + return obj; + + return null; + } + + public void releaseRemoteObj(ManagedObj obj) + { + assert lock.isWriteLockedByCurrentThread(); + + if (!obj.isReferenced()) + { + try + { + obj.port.iManagedObjectRefRelease(obj.objId); + } + catch (InvalidObjectFaultMsg e) + { + throw new WebServiceException(e); + } + catch (RuntimeFaultMsg e) + { + throw new WebServiceException(e); + } + finally + { + this.map.remove(obj.objId); + } + } + } + + /** + * An object which is living on the server side. This can be referenced + * by multiple stub objects here. + */ + static class ManagedObj + { + private final String objId; + private final VboxPortType port; + private final ConcurrentLinkedQueue refQ; + + ManagedObj(String objId, VboxPortType port) + { + this.objId = objId; + this.port = port; + this.refQ = new ConcurrentLinkedQueue(); + } + + public void addObject(ManagedObjRef obj) + { + this.refQ.add(obj); + } + + public void removeObject(ManagedObjRef obj) + { + this.refQ.remove(obj); + } + + public boolean isReferenced() + { + return !this.refQ.isEmpty(); + } + } + + /** + * A private class extending WeakReference to get notified about garbage + * collected stub objects. + */ + static class ManagedObjRef extends WeakReference + { + final String objId; + + ManagedObjRef(IUnknown obj) + { + super(obj, refQ); + this.objId = obj.getWrapped(); + } + } + + /** + * A private class implementing a thread getting notified + * about garbage collected objects so it can release the object on the + * server side if it is not used anymore. + */ + static class ObjRefMgrCleanupThread extends Thread + { + ObjectRefManager objRefMgr; + int cStubsReleased; + int cStubsReleaseThreshold; + HashMap mapToRelease = new HashMap(); + + ObjRefMgrCleanupThread(ObjectRefManager objRefMgr) + { + init(objRefMgr, 500); + } + + ObjRefMgrCleanupThread(ObjectRefManager objRefMgr, int cStubsReleaseThreshold) + { + init(objRefMgr, cStubsReleaseThreshold); + } + + private void init(ObjectRefManager objRefMgr, int cStubsReleaseThreshold) + { + this.objRefMgr = objRefMgr; + this.cStubsReleased = 0; + this.cStubsReleaseThreshold = cStubsReleaseThreshold; + setName("ObjectRefManager-VBoxWSObjRefGcThrd"); + /* + * setDaemon() makes sure the jvm exits and is not blocked + * if the thread is still running so we don't have to care about + * tearing it down. + */ + setDaemon(true); + } + + public void run() + { + while (true) + { + while (cStubsReleased < cStubsReleaseThreshold) + { + try + { + /* Accumulate a few objects before we start. */ + while (cStubsReleased < cStubsReleaseThreshold) + { + ManagedObjRef ref = (ManagedObjRef)refQ.remove(); + ManagedObj obj = this.objRefMgr.unregisterObj(ref); + /* + * If the server side object is not referenced anymore + * promote to map for releasing later. + */ + if (obj != null && !mapToRelease.containsKey(ref.objId)) + mapToRelease.put(ref.objId, obj); + + cStubsReleased++; + } + } + catch (InterruptedException e) + { /* ignore */ } + catch (javax.xml.ws.WebServiceException e) + { /* ignore */ } + } + + /* + * After we released enough stubs we go over all non referenced + * server side objects and release them if they were not + * referenced again in between. + */ + cStubsReleased = 0; + if (!mapToRelease.isEmpty()) + { + this.objRefMgr.startObjRelease(); + try + { + Iterator it = mapToRelease.values().iterator(); + while (it.hasNext()) + { + ManagedObj obj = it.next(); + this.objRefMgr.releaseRemoteObj(obj); + } + + mapToRelease.clear(); + } + catch (javax.xml.ws.WebServiceException e) + { /* ignore */ } + finally + { + this.objRefMgr.endObjRelease(); + } + } + } + } + } +} + +class VBoxTLSSocketFactory extends SSLSocketFactory +{ + private final SSLSocketFactory sf; + + private void setupSocket(SSLSocket s) + { + String[] oldproto = s.getEnabledProtocols(); + List protolist = new ArrayList(); + for (int i = 0; i < oldproto.length; i++) + if (oldproto[i].toUpperCase().startsWith("TLS")) + protolist.add(oldproto[i]); + String[] newproto = protolist.toArray(new String[protolist.size()]); + s.setEnabledProtocols(newproto); + } + + public VBoxTLSSocketFactory() + { + SSLSocketFactory tmp = null; + try + { + SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, null, null); + tmp = sc.getSocketFactory(); + } + catch (Exception e) + { + e.printStackTrace(); + } + sf = tmp; + } + + public static SocketFactory getDefault() + { + return new VBoxTLSSocketFactory(); + } + + public Socket createSocket(Socket socket, String host, int port, + boolean autoClose) throws IOException, UnknownHostException + { + SSLSocket s = (SSLSocket)sf.createSocket(socket, host, port, autoClose); + setupSocket(s); + return s; + } + + public Socket createSocket() throws IOException + { + SSLSocket s = (SSLSocket)sf.createSocket(); + setupSocket(s); + return s; + } + + public Socket createSocket(InetAddress host, int port) throws IOException + { + SSLSocket s = (SSLSocket)sf.createSocket(host, port); + setupSocket(s); + return s; + } + + public Socket createSocket(InetAddress address, int port, + InetAddress localAddress, int localPort) throws IOException + { + SSLSocket s = (SSLSocket)sf.createSocket(address, port, localAddress, localPort); + setupSocket(s); + return s; + } + + public Socket createSocket(String host, int port) throws IOException, UnknownHostException + { + SSLSocket s = (SSLSocket)sf.createSocket(host, port); + setupSocket(s); + return s; + } + + public Socket createSocket(String host, int port, + InetAddress localHost, int localPort) throws IOException, UnknownHostException + { + SSLSocket s = (SSLSocket)sf.createSocket(host, port, localHost, localPort); + setupSocket(s); + return s; + } + + public String[] getDefaultCipherSuites() + { + return sf.getDefaultCipherSuites(); + } + + public String[] getSupportedCipherSuites() + { + return sf.getSupportedCipherSuites(); + } +} + + +public class VirtualBoxManager +{ + private static PortPool pool = new PortPool(true); + private static final ObjectRefManager objMgr = new ObjectRefManager(); + protected VboxPortType port; + + private IVirtualBox vbox; + + private VirtualBoxManager() + { + } + + public static void initPerThread() + { + } + + public static void deinitPerThread() + { + } + + public void connect(String url, String username, String passwd) + { + this.port = pool.getPort(); + try + { + ((BindingProvider)port).getRequestContext(). + put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url); + + // Unfortunately there is no official way to make JAX-WS use + // TLS only, which means that a rather tedious approach is + // unavoidable (implementing a TLS only SSLSocketFactory, + // because the default one associated with a TLS SSLContext + // happily uses SSLv2/3 handshakes, which make TLS servers + // drop the connection), and additionally a not standardized, + // shotgun approach is needed to make the relevant JAX-WS + // implementations use this factory. + VBoxTLSSocketFactory sf = new VBoxTLSSocketFactory(); + ((BindingProvider)port).getRequestContext(). + put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", sf); + ((BindingProvider)port).getRequestContext(). + put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", sf); + + String handle = port.iWebsessionManagerLogon(username, passwd); + this.objMgr.preventObjRelease(); + try + { + this.vbox = new IVirtualBox(handle, this.objMgr, port); + } + finally + { + this.objMgr.allowObjRelease(); + } + } + catch (Throwable t) + { + if (this.port != null && pool != null) + { + pool.releasePort(this.port); + this.port = null; + } + // we have to throw smth derived from RuntimeException + throw new VBoxException(t.getMessage(), t, this.objMgr, this.port); + } + } + + public void connect(String url, String username, String passwd, + Map requestContext, Map responseContext) + { + this.port = pool.getPort(); + try + { + ((BindingProvider)port).getRequestContext(); + if (requestContext != null) + ((BindingProvider)port).getRequestContext().putAll(requestContext); + + if (responseContext != null) + ((BindingProvider)port).getResponseContext().putAll(responseContext); + + ((BindingProvider)port).getRequestContext(). + put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url); + String handle = port.iWebsessionManagerLogon(username, passwd); + this.objMgr.preventObjRelease(); + try + { + this.vbox = new IVirtualBox(handle, this.objMgr, port); + } + finally + { + this.objMgr.allowObjRelease(); + } + } + catch (Throwable t) + { + if (this.port != null && pool != null) + { + pool.releasePort(this.port); + this.port = null; + } + // we have to throw smth derived from RuntimeException + throw new VBoxException(t.getMessage(), t, this.objMgr, this.port); + } + } + + public void disconnect() + { + if (this.port == null) + return; + + try + { + if (this.vbox != null && port != null) + port.iWebsessionManagerLogoff(this.vbox.getWrapped()); + } + catch (InvalidObjectFaultMsg e) + { + throw new VBoxException(e.getMessage(), e, this.objMgr, this.port); + } + catch (RuntimeFaultMsg e) + { + throw new VBoxException(e.getMessage(), e, this.objMgr, this.port); + } + finally + { + if (this.port != null) + { + pool.releasePort(this.port); + this.port = null; + } + } + } + + public String getClientAPIVersion() + { + return "]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/VBox/Main/glue/initterm.cpp b/src/VBox/Main/glue/initterm.cpp new file mode 100644 index 00000000..cdf8efe9 --- /dev/null +++ b/src/VBox/Main/glue/initterm.cpp @@ -0,0 +1,861 @@ +/* $Id: initterm.cpp $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - Initialization and Termination. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#define LOG_GROUP LOG_GROUP_MAIN +#if !defined(VBOX_WITH_XPCOM) + +# if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600 +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x600 /* GetModuleHandleExW */ +# endif +# include +# include +# include +# include + +#else /* !defined(VBOX_WITH_XPCOM) */ + +# include + +# include +# include +# include +# include +# include + +# include +# include +# include + +#endif /* !defined(VBOX_WITH_XPCOM) */ + +#include "VBox/com/com.h" +#include "VBox/com/assert.h" +#include "VBox/com/NativeEventQueue.h" +#include "VBox/com/AutoLock.h" + +#include "../include/LoggingNew.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace com +{ + +#if defined(VBOX_WITH_XPCOM) + +class DirectoryServiceProvider : public nsIDirectoryServiceProvider +{ +public: + + NS_DECL_ISUPPORTS + + DirectoryServiceProvider() + : mCompRegLocation(NULL), mXPTIDatLocation(NULL) + , mComponentDirLocation(NULL), mCurrProcDirLocation(NULL) + {} + + virtual ~DirectoryServiceProvider(); + + HRESULT init(const char *aCompRegLocation, + const char *aXPTIDatLocation, + const char *aComponentDirLocation, + const char *aCurrProcDirLocation); + + NS_DECL_NSIDIRECTORYSERVICEPROVIDER + +private: + /** @remarks This is not a UTF-8 string. */ + char *mCompRegLocation; + /** @remarks This is not a UTF-8 string. */ + char *mXPTIDatLocation; + /** @remarks This is not a UTF-8 string. */ + char *mComponentDirLocation; + /** @remarks This is not a UTF-8 string. */ + char *mCurrProcDirLocation; +}; + +NS_IMPL_ISUPPORTS1(DirectoryServiceProvider, nsIDirectoryServiceProvider) + +DirectoryServiceProvider::~DirectoryServiceProvider() +{ + if (mCompRegLocation) + { + RTStrFree(mCompRegLocation); + mCompRegLocation = NULL; + } + if (mXPTIDatLocation) + { + RTStrFree(mXPTIDatLocation); + mXPTIDatLocation = NULL; + } + if (mComponentDirLocation) + { + RTStrFree(mComponentDirLocation); + mComponentDirLocation = NULL; + } + if (mCurrProcDirLocation) + { + RTStrFree(mCurrProcDirLocation); + mCurrProcDirLocation = NULL; + } +} + +/** + * @param aCompRegLocation Path to compreg.dat, in Utf8. + * @param aXPTIDatLocation Path to xpti.data, in Utf8. + */ +HRESULT +DirectoryServiceProvider::init(const char *aCompRegLocation, + const char *aXPTIDatLocation, + const char *aComponentDirLocation, + const char *aCurrProcDirLocation) +{ + AssertReturn(aCompRegLocation, NS_ERROR_INVALID_ARG); + AssertReturn(aXPTIDatLocation, NS_ERROR_INVALID_ARG); + +/** @todo r=bird: Gotta check how this encoding stuff plays out on darwin! + * We get down to [VBoxNsxp]NS_NewNativeLocalFile and that file isn't + * nsLocalFileUnix.cpp on 32-bit darwin. On 64-bit darwin it's a question + * of what we're doing in IPRT and such... We should probably add a + * RTPathConvertToNative for use here. */ + int vrc = RTStrUtf8ToCurrentCP(&mCompRegLocation, aCompRegLocation); + if (RT_SUCCESS(vrc)) + vrc = RTStrUtf8ToCurrentCP(&mXPTIDatLocation, aXPTIDatLocation); + if (RT_SUCCESS(vrc) && aComponentDirLocation) + vrc = RTStrUtf8ToCurrentCP(&mComponentDirLocation, aComponentDirLocation); + if (RT_SUCCESS(vrc) && aCurrProcDirLocation) + vrc = RTStrUtf8ToCurrentCP(&mCurrProcDirLocation, aCurrProcDirLocation); + + return RT_SUCCESS(vrc) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + +NS_IMETHODIMP +DirectoryServiceProvider::GetFile(const char *aProp, + PRBool *aPersistent, + nsIFile **aRetval) +{ + nsCOMPtr localFile; + nsresult rv = NS_ERROR_FAILURE; + + *aRetval = nsnull; + *aPersistent = PR_TRUE; + + const char *fileLocation = NULL; + + if (strcmp(aProp, NS_XPCOM_COMPONENT_REGISTRY_FILE) == 0) + fileLocation = mCompRegLocation; + else if (strcmp(aProp, NS_XPCOM_XPTI_REGISTRY_FILE) == 0) + fileLocation = mXPTIDatLocation; + else if (mComponentDirLocation && strcmp(aProp, NS_XPCOM_COMPONENT_DIR) == 0) + fileLocation = mComponentDirLocation; + else if (mCurrProcDirLocation && strcmp(aProp, NS_XPCOM_CURRENT_PROCESS_DIR) == 0) + fileLocation = mCurrProcDirLocation; + else + return NS_ERROR_FAILURE; + + rv = NS_NewNativeLocalFile(nsEmbedCString(fileLocation), + PR_TRUE, getter_AddRefs(localFile)); + if (NS_FAILED(rv)) + return rv; + + return localFile->QueryInterface(NS_GET_IID(nsIFile), (void **)aRetval); +} + +/** + * Global XPCOM initialization flag (we maintain it ourselves since XPCOM + * doesn't provide such functionality) + */ +static bool volatile gIsXPCOMInitialized = false; + +/** + * Number of Initialize() calls on the main thread. + */ +static unsigned int gXPCOMInitCount = 0; + +#else /* !defined(VBOX_WITH_XPCOM) */ + +/** + * Replacement function for the InvokeStub method for the IRundown stub. + */ +static HRESULT STDMETHODCALLTYPE +Rundown_InvokeStub(IRpcStubBuffer *pThis, RPCOLEMESSAGE *pMsg, IRpcChannelBuffer *pBuf) RT_NOTHROW_DEF +{ + /* + * Our mission here is to prevent remote calls to methods #8 and #9, + * as these contain raw pointers to callback functions. + * + * Note! APIs like I_RpcServerInqTransportType, I_RpcBindingInqLocalClientPID + * and RpcServerInqCallAttributesW are not usable in this context without + * a rpc binding handle (latter two). + * + * P.S. In more recent windows versions, the buffer implements a interface + * IID_IRpcChannelBufferMarshalingContext (undocumented) which has a + * GetIMarshallingContextAttribute() method that will return the client PID + * when asking for attribute #0x8000000e. + */ + uint32_t const iMethod = pMsg->iMethod & 0xffff; /* Uncertain, but there are hints that the upper bits are flags. */ + HRESULT hrc; + if ( ( iMethod != 8 + && iMethod != 9) + || (pMsg->rpcFlags & RPCFLG_LOCAL_CALL) ) + hrc = CStdStubBuffer_Invoke(pThis, pMsg, pBuf); + else + { + LogRel(("Rundown_InvokeStub: Rejected call to CRundown::%s: rpcFlags=%#x cbBuffer=%#x dataRepresentation=%d buffer=%p:{%.*Rhxs} reserved1=%p reserved2={%p,%p,%p,%p,%p}\n", + pMsg->iMethod == 8 ? "DoCallback" : "DoNonreentrantCallback", pMsg->rpcFlags, pMsg->cbBuffer, + pMsg->dataRepresentation, pMsg->Buffer, RT_VALID_PTR(pMsg->Buffer) ? pMsg->cbBuffer : 0, pMsg->Buffer, + pMsg->reserved1, pMsg->reserved2[0], pMsg->reserved2[1], pMsg->reserved2[2], pMsg->reserved2[3], pMsg->reserved2[4])); + hrc = E_ACCESSDENIED; + } + return hrc; +} + +/** + * Replacement function for the InvokeStub method for the IDLLHost stub. + */ +static HRESULT STDMETHODCALLTYPE +DLLHost_InvokeStub(IRpcStubBuffer *pThis, RPCOLEMESSAGE *pMsg, IRpcChannelBuffer *pBuf) RT_NOTHROW_DEF +{ + /* + * Our mission here is to prevent remote calls to this interface as method #3 + * contain a raw pointer an DllGetClassObject function. There are only that + * method in addition to the IUnknown stuff, and it's ASSUMED that it's + * process internal only (cross apartment stuff). + */ + uint32_t const iMethod = pMsg->iMethod & 0xffff; /* Uncertain, but there are hints that the upper bits are flags. */ + HRESULT hrc; + if (pMsg->rpcFlags & RPCFLG_LOCAL_CALL) + hrc = CStdStubBuffer_Invoke(pThis, pMsg, pBuf); + else + { + LogRel(("DLLHost_InvokeStub: Rejected call to CDLLHost::%s: rpcFlags=%#x cbBuffer=%#x dataRepresentation=%d buffer=%p:{%.*Rhxs} reserved1=%p reserved2={%p,%p,%p,%p,%p}\n", + pMsg->iMethod == 0 ? "QueryInterface" : + pMsg->iMethod == 1 ? "AddRef" : + pMsg->iMethod == 2 ? "ReleaseRef" : + pMsg->iMethod == 3 ? "DllGetClassObject" : "Unknown", pMsg->rpcFlags, pMsg->cbBuffer, + pMsg->dataRepresentation, pMsg->Buffer, RT_VALID_PTR(pMsg->Buffer) ? pMsg->cbBuffer : 0, pMsg->Buffer, + pMsg->reserved1, pMsg->reserved2[0], pMsg->reserved2[1], pMsg->reserved2[2], pMsg->reserved2[3], pMsg->reserved2[4])); + hrc = E_ACCESSDENIED; + } + return hrc; +} + +/** + * Replaces the IRundown InvokeStub method with Rundown_InvokeStub so we can + * reject remote calls to a couple of misdesigned methods. + * + * Also replaces the IDLLHost for the same reasons. + */ +void PatchComBugs(void) +{ + static volatile bool s_fPatched = false; + if (s_fPatched) + return; + + /* + * The combase.dll / ole32.dll is exporting a DllGetClassObject function + * that is implemented using NdrDllGetClassObject just like our own + * proxy/stub DLL. This means we can get at the stub interface lists, + * since what NdrDllGetClassObject has CStdPSFactoryBuffer as layout. + * + * Note! Tried using CoRegisterPSClsid instead of this mess, but no luck. + */ + /* Locate the COM DLL, it must be loaded by now: */ + HMODULE hmod = GetModuleHandleW(L"COMBASE.DLL"); + if (!hmod) + hmod = GetModuleHandleW(L"OLE32.DLL"); /* w7 */ + AssertReturnVoid(hmod != NULL); + + /* Resolve the class getter: */ + LPFNGETCLASSOBJECT pfnGetClassObject = (LPFNGETCLASSOBJECT)GetProcAddress(hmod, "DllGetClassObject"); + AssertReturnVoid(pfnGetClassObject != NULL); + + /* Get the factory instance: */ + static const CLSID s_PSOlePrx32ClsId = {0x00000320,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; + CStdPSFactoryBuffer *pFactoryBuffer = NULL; + HRESULT hrc = pfnGetClassObject(s_PSOlePrx32ClsId, IID_IPSFactoryBuffer, (void **)&pFactoryBuffer); + AssertMsgReturnVoid(SUCCEEDED(hrc), ("hrc=%Rhrc\n", hrc)); + AssertReturnVoid(pFactoryBuffer != NULL); + + /* + * Search thru the file list for the interface we want to patch. + */ + static const IID s_IID_Rundown = {0x00000134,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; + static const IID s_IID_DLLHost = {0x00000141,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; + decltype(CStdStubBuffer_Invoke) *pfnInvoke = (decltype(pfnInvoke))GetProcAddress(hmod, "CStdStubBuffer_Invoke"); + if (!pfnInvoke) + pfnInvoke = (decltype(pfnInvoke))GetProcAddress(GetModuleHandleW(L"RPCRT4.DLL"), "CStdStubBuffer_Invoke"); + + unsigned cPatched = 0; + unsigned cAlreadyPatched = 0; + Assert(pFactoryBuffer->pProxyFileList != NULL); + for (ProxyFileInfo const **ppCur = pFactoryBuffer->pProxyFileList; *ppCur != NULL; ppCur++) + { + ProxyFileInfo const *pCur = *ppCur; + + if (pCur->pStubVtblList) + { + for (PCInterfaceStubVtblList const *ppCurStub = pCur->pStubVtblList; *ppCurStub != NULL; ppCurStub++) + { + PCInterfaceStubVtblList const pCurStub = *ppCurStub; + IID const *piid = pCurStub->header.piid; + if (piid) + { + if (IsEqualIID(*piid, s_IID_Rundown)) + { + if (pCurStub->Vtbl.Invoke == pfnInvoke) + { + DWORD fOld = 0; + if (VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), PAGE_READWRITE, &fOld)) + { + pCurStub->Vtbl.Invoke = Rundown_InvokeStub; + VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), fOld, &fOld); + cPatched++; + } + else + AssertMsgFailed(("%d\n", GetLastError())); + } + else + cAlreadyPatched++; + } + else if (IsEqualIID(*piid, s_IID_DLLHost)) + { + if (pCurStub->Vtbl.Invoke == pfnInvoke) + { + DWORD fOld = 0; + if (VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), PAGE_READWRITE, &fOld)) + { + pCurStub->Vtbl.Invoke = DLLHost_InvokeStub; + VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), fOld, &fOld); + cPatched++; + } + else + AssertMsgFailed(("%d\n", GetLastError())); + } + else + cAlreadyPatched++; + } + } + } + } + } + + /* done */ + pFactoryBuffer->lpVtbl->Release((IPSFactoryBuffer *)pFactoryBuffer); + + /* + * If we patched anything we should try prevent being unloaded. + */ + if (cPatched > 0) + { + s_fPatched = true; + HMODULE hmodSelf; + AssertLogRelMsg(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, + (LPCWSTR)(uintptr_t)Rundown_InvokeStub, &hmodSelf), + ("last error: %u; Rundown_InvokeStub=%p\n", GetLastError(), Rundown_InvokeStub)); + } + AssertLogRelMsg(cAlreadyPatched + cPatched >= 2, + ("COM patching of IRundown/IDLLHost failed! (%d+%d)\n", cAlreadyPatched, cPatched)); +} + + +/** + * The COM main thread handle. (The first caller of com::Initialize().) + */ +static RTTHREAD volatile gCOMMainThread = NIL_RTTHREAD; + +/** + * Number of Initialize() calls on the main thread. + */ +static uint32_t gCOMMainInitCount = 0; + +#endif /* !defined(VBOX_WITH_XPCOM) */ + + +/** + * Initializes the COM runtime. + * + * This method must be called on each thread of the client application that + * wants to access COM facilities. The initialization must be performed before + * calling any other COM method or attempting to instantiate COM objects. + * + * On platforms using XPCOM, this method uses the following scheme to search for + * XPCOM runtime: + * + * 1. If the VBOX_APP_HOME environment variable is set, the path it specifies + * is used to search XPCOM libraries and components. If this method fails to + * initialize XPCOM runtime using this path, it will immediately return a + * failure and will NOT check for other paths as described below. + * + * 2. If VBOX_APP_HOME is not set, this methods tries the following paths in the + * given order: + * + * a) Compiled-in application data directory (as returned by + * RTPathAppPrivateArch()) + * b) "/usr/lib/virtualbox" (Linux only) + * c) "/opt/VirtualBox" (Linux only) + * + * The first path for which the initialization succeeds will be used. + * + * On MS COM platforms, the COM runtime is provided by the system and does not + * need to be searched for. + * + * Once the COM subsystem is no longer necessary on a given thread, Shutdown() + * must be called to free resources allocated for it. Note that a thread may + * call Initialize() several times but for each of tese calls there must be a + * corresponding Shutdown() call. + * + * @return S_OK on success and a COM result code in case of failure. + */ +HRESULT Initialize(uint32_t fInitFlags /*=VBOX_COM_INIT_F_DEFAULT*/) +{ + HRESULT hrc = E_FAIL; + +#if !defined(VBOX_WITH_XPCOM) + +# ifdef VBOX_WITH_AUTO_COM_REG_UPDATE + /* + * First time we're called in a process, we refresh the VBox COM + * registrations. Use a global mutex to prevent updating when there are + * API users already active, as that could lead to a bit of a mess. + */ + if ( (fInitFlags & VBOX_COM_INIT_F_AUTO_REG_UPDATE) + && gCOMMainThread == NIL_RTTHREAD) + { + SetLastError(ERROR_SUCCESS); + HANDLE hLeakIt = CreateMutexW(NULL/*pSecAttr*/, FALSE, L"Global\\VirtualBoxComLazyRegistrationMutant"); + DWORD dwErr = GetLastError(); + AssertMsg(dwErr == ERROR_SUCCESS || dwErr == ERROR_ALREADY_EXISTS || dwErr == ERROR_ACCESS_DENIED, ("%u\n", dwErr)); + if (dwErr == ERROR_SUCCESS) + { + char szPath[RTPATH_MAX]; + int vrc = RTPathAppPrivateArch(szPath, sizeof(szPath)); + if (RT_SUCCESS(vrc)) +# ifndef VBOX_IN_32_ON_64_MAIN_API + vrc = RTPathAppend(szPath, sizeof(szPath), + RT_MAKE_U64(((PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA)->NtMinorVersion, + ((PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA)->NtMajorVersion) + >= RT_MAKE_U64(1/*Lo*/,6/*Hi*/) + ? "VBoxProxyStub.dll" : "VBoxProxyStubLegacy.dll"); +# else + vrc = RTPathAppend(szPath, sizeof(szPath), "x86\\VBoxProxyStub-x86.dll"); +# endif + if (RT_SUCCESS(vrc)) + { + RTLDRMOD hMod; + vrc = RTLdrLoad(szPath, &hMod); + if (RT_SUCCESS(vrc)) + { + union + { + void *pv; + DECLCALLBACKMEMBER(uint32_t, pfnRegUpdate,(void)); + } u; + vrc = RTLdrGetSymbol(hMod, "VbpsUpdateRegistrations", &u.pv); + if (RT_SUCCESS(vrc)) + u.pfnRegUpdate(); + /* Just keep it loaded. */ + } + } + Assert(hLeakIt != NULL); NOREF(hLeakIt); + } + } +# endif + + /* + * We initialize COM in GUI thread in STA, to be compliant with QT and + * OLE requirments (for example to allow D&D), while other threads + * initialized in regular MTA. To allow fast proxyless access from + * GUI thread to COM objects, we explicitly provide our COM objects + * with free threaded marshaller. + * !!!!! Please think twice before touching this code !!!!! + */ + DWORD flags = fInitFlags & VBOX_COM_INIT_F_GUI + ? COINIT_APARTMENTTHREADED + | COINIT_SPEED_OVER_MEMORY + : COINIT_MULTITHREADED + | COINIT_DISABLE_OLE1DDE + | COINIT_SPEED_OVER_MEMORY; + + hrc = CoInitializeEx(NULL, flags); + + /* the overall result must be either S_OK or S_FALSE (S_FALSE means + * "already initialized using the same apartment model") */ + AssertMsg(hrc == S_OK || hrc == S_FALSE, ("hrc=%08X\n", hrc)); + +#if defined(VBOX_WITH_SDS) + // Setup COM Security to enable impersonation + HRESULT hrcGUICoInitializeSecurity = CoInitializeSecurity(NULL, + -1, + NULL, + NULL, + RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE, + NULL); + NOREF(hrcGUICoInitializeSecurity); + Assert(SUCCEEDED(hrcGUICoInitializeSecurity) || hrcGUICoInitializeSecurity == RPC_E_TOO_LATE); +#endif + + /* + * IRundown has unsafe two methods we need to patch to prevent remote access. + * Do that before we start using COM and open ourselves to possible attacks. + */ + if (!(fInitFlags & VBOX_COM_INIT_F_NO_COM_PATCHING)) + PatchComBugs(); + + /* To be flow compatible with the XPCOM case, we return here if this isn't + * the main thread or if it isn't its first initialization call. + * Note! CoInitializeEx and CoUninitialize does it's own reference + * counting, so this exercise is entirely for the EventQueue init. */ + bool fRc; + RTTHREAD hSelf = RTThreadSelf(); + if (hSelf != NIL_RTTHREAD) + ASMAtomicCmpXchgHandle(&gCOMMainThread, hSelf, NIL_RTTHREAD, fRc); + else + fRc = false; + + if (fInitFlags & VBOX_COM_INIT_F_GUI) + Assert(RTThreadIsMain(hSelf)); + + if (!fRc) + { + if ( gCOMMainThread == hSelf + && SUCCEEDED(hrc)) + gCOMMainInitCount++; + + AssertComRC(hrc); + return hrc; + } + Assert(RTThreadIsMain(hSelf)); + + /* this is the first main thread initialization */ + Assert(gCOMMainInitCount == 0); + if (SUCCEEDED(hrc)) + gCOMMainInitCount = 1; + +#else /* !defined(VBOX_WITH_XPCOM) */ + + /* Unused here */ + RT_NOREF(fInitFlags); + + if (ASMAtomicXchgBool(&gIsXPCOMInitialized, true) == true) + { + /* XPCOM is already initialized on the main thread, no special + * initialization is necessary on additional threads. Just increase + * the init counter if it's a main thread again (to correctly support + * nested calls to Initialize()/Shutdown() for compatibility with + * Win32). */ + + nsCOMPtr eventQ; + hrc = NS_GetMainEventQ(getter_AddRefs(eventQ)); + + if (NS_SUCCEEDED(hrc)) + { + PRBool isOnMainThread = PR_FALSE; + hrc = eventQ->IsOnCurrentThread(&isOnMainThread); + if (NS_SUCCEEDED(hrc) && isOnMainThread) + ++gXPCOMInitCount; + } + + AssertComRC(hrc); + return hrc; + } + Assert(RTThreadIsMain(RTThreadSelf())); + + /* this is the first initialization */ + gXPCOMInitCount = 1; + + /* prepare paths for registry files */ + char szCompReg[RTPATH_MAX]; + char szXptiDat[RTPATH_MAX]; + + int vrc = GetVBoxUserHomeDirectory(szCompReg, sizeof(szCompReg)); + if (vrc == VERR_ACCESS_DENIED) + return NS_ERROR_FILE_ACCESS_DENIED; + AssertRCReturn(vrc, NS_ERROR_FAILURE); + vrc = RTStrCopy(szXptiDat, sizeof(szXptiDat), szCompReg); + AssertRCReturn(vrc, NS_ERROR_FAILURE); +# ifdef VBOX_IN_32_ON_64_MAIN_API + vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg-x86.dat"); + AssertRCReturn(vrc, NS_ERROR_FAILURE); + vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti-x86.dat"); + AssertRCReturn(vrc, NS_ERROR_FAILURE); +# else + vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg.dat"); + AssertRCReturn(vrc, NS_ERROR_FAILURE); + vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti.dat"); + AssertRCReturn(vrc, NS_ERROR_FAILURE); +# endif + + LogFlowFunc(("component registry : \"%s\"\n", szCompReg)); + LogFlowFunc(("XPTI data file : \"%s\"\n", szXptiDat)); + + static const char *kAppPathsToProbe[] = + { + NULL, /* 0: will use VBOX_APP_HOME */ + NULL, /* 1: will try RTPathAppPrivateArch(), correctly installed release builds will never go further */ + NULL, /* 2: will try parent directory of RTPathAppPrivateArch(), only for testcases in non-hardened builds */ + /* There used to be hard coded paths, but they only caused trouble + * because they often led to mixing of builds or even versions. + * If you feel tempted to add anything here, think again. They would + * only be used if option 1 would not work, which is a sign of a big + * problem, as it returns a fixed location defined at compile time. + * It is better to fail than blindly trying to cover the problem. */ + }; + + /* Find out the directory where VirtualBox binaries are located */ + for (size_t i = 0; i < RT_ELEMENTS(kAppPathsToProbe); ++ i) + { + char szAppHomeDir[RTPATH_MAX]; + + if (i == 0) + { + /* Use VBOX_APP_HOME if present */ + vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_APP_HOME", szAppHomeDir, sizeof(szAppHomeDir), NULL); + if (vrc == VERR_ENV_VAR_NOT_FOUND) + continue; + AssertRC(vrc); + } + else if (i == 1) + { + /* Use RTPathAppPrivateArch() first */ + vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir)); + AssertRC(vrc); + } + else if (i == 2) + { +# ifdef VBOX_WITH_HARDENING + continue; +# else /* !VBOX_WITH_HARDENING */ + /* Use parent of RTPathAppPrivateArch() if ends with "testcase" */ + vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir)); + AssertRC(vrc); + vrc = RTPathStripTrailingSlash(szAppHomeDir); + AssertRC(vrc); + char *filename = RTPathFilename(szAppHomeDir); + if (!filename || strcmp(filename, "testcase")) + continue; + RTPathStripFilename(szAppHomeDir); +# endif /* !VBOX_WITH_HARDENING */ + } + else + { + /* Iterate over all other paths */ + RTStrCopy(szAppHomeDir, sizeof(szAppHomeDir), kAppPathsToProbe[i]); + vrc = VINF_SUCCESS; + } + if (RT_FAILURE(vrc)) + { + hrc = NS_ERROR_FAILURE; + continue; + } + char szCompDir[RTPATH_MAX]; + vrc = RTStrCopy(szCompDir, sizeof(szCompDir), szAppHomeDir); + if (RT_FAILURE(vrc)) + { + hrc = NS_ERROR_FAILURE; + continue; + } + vrc = RTPathAppend(szCompDir, sizeof(szCompDir), "components"); + if (RT_FAILURE(vrc)) + { + hrc = NS_ERROR_FAILURE; + continue; + } + LogFlowFunc(("component directory : \"%s\"\n", szCompDir)); + + nsCOMPtr dsProv; + dsProv = new DirectoryServiceProvider(); + if (dsProv) + hrc = dsProv->init(szCompReg, szXptiDat, szCompDir, szAppHomeDir); + else + hrc = NS_ERROR_OUT_OF_MEMORY; + if (NS_FAILED(hrc)) + break; + + /* Setup the application path for NS_InitXPCOM2. Note that we properly + * answer the NS_XPCOM_CURRENT_PROCESS_DIR query in our directory + * service provider but it seems to be activated after the directory + * service is used for the first time (see the source NS_InitXPCOM2). So + * use the same value here to be on the safe side. */ + nsCOMPtr appDir; + { + char *appDirCP = NULL; + vrc = RTStrUtf8ToCurrentCP(&appDirCP, szAppHomeDir); + if (RT_SUCCESS(vrc)) + { + nsCOMPtr file; + hrc = NS_NewNativeLocalFile(nsEmbedCString(appDirCP), PR_FALSE, getter_AddRefs(file)); + if (NS_SUCCEEDED(hrc)) + appDir = do_QueryInterface(file, &hrc); + + RTStrFree(appDirCP); + } + else + hrc = NS_ERROR_FAILURE; + } + if (NS_FAILED(hrc)) + break; + + /* Set VBOX_XPCOM_HOME to the same app path to make XPCOM sources that + * still use it instead of the directory service happy */ + vrc = RTEnvSetEx(RTENV_DEFAULT, "VBOX_XPCOM_HOME", szAppHomeDir); + AssertRC(vrc); + + /* Finally, initialize XPCOM */ + { + nsCOMPtr serviceManager; + hrc = NS_InitXPCOM2(getter_AddRefs(serviceManager), appDir, dsProv); + if (NS_SUCCEEDED(hrc)) + { + nsCOMPtr registrar = do_QueryInterface(serviceManager, &hrc); + if (NS_SUCCEEDED(hrc)) + { + hrc = registrar->AutoRegister(nsnull); + if (NS_SUCCEEDED(hrc)) + { + /* We succeeded, stop probing paths */ + LogFlowFunc(("Succeeded.\n")); + break; + } + } + } + } + + /* clean up before the new try */ + HRESULT hrc2 = NS_ShutdownXPCOM(nsnull); + if (SUCCEEDED(hrc)) + hrc = hrc2; + + if (i == 0) + { + /* We failed with VBOX_APP_HOME, don't probe other paths */ + break; + } + } + +#endif /* !defined(VBOX_WITH_XPCOM) */ + + AssertComRCReturnRC(hrc); + + // for both COM and XPCOM, we only get here if this is the main thread; + // only then initialize the autolock system (AutoLock.cpp) + Assert(RTThreadIsMain(RTThreadSelf())); + util::InitAutoLockSystem(); + + /* + * Init the main event queue (ASSUMES it cannot fail). + */ + if (SUCCEEDED(hrc)) + NativeEventQueue::init(); + + return hrc; +} + +HRESULT Shutdown() +{ + HRESULT hrc = S_OK; + +#if !defined(VBOX_WITH_XPCOM) + + /* EventQueue::uninit reference counting fun. */ + RTTHREAD hSelf = RTThreadSelf(); + if ( hSelf == gCOMMainThread + && hSelf != NIL_RTTHREAD) + { + if (-- gCOMMainInitCount == 0) + { + NativeEventQueue::uninit(); + ASMAtomicWriteHandle(&gCOMMainThread, NIL_RTTHREAD); + } + } + + CoUninitialize(); + +#else /* !defined(VBOX_WITH_XPCOM) */ + + nsCOMPtr eventQ; + hrc = NS_GetMainEventQ(getter_AddRefs(eventQ)); + + if (NS_SUCCEEDED(hrc) || hrc == NS_ERROR_NOT_AVAILABLE) + { + /* NS_ERROR_NOT_AVAILABLE seems to mean that + * nsIEventQueue::StopAcceptingEvents() has been called (see + * nsEventQueueService.cpp). We hope that this error code always means + * just that in this case and assume that we're on the main thread + * (it's a kind of unexpected behavior if a non-main thread ever calls + * StopAcceptingEvents() on the main event queue). */ + + PRBool isOnMainThread = PR_FALSE; + if (NS_SUCCEEDED(hrc)) + { + hrc = eventQ->IsOnCurrentThread(&isOnMainThread); + eventQ = nsnull; /* early release before shutdown */ + } + else + { + isOnMainThread = RTThreadIsMain(RTThreadSelf()); + hrc = NS_OK; + } + + if (NS_SUCCEEDED(hrc) && isOnMainThread) + { + /* only the main thread needs to uninitialize XPCOM and only if + * init counter drops to zero */ + if (--gXPCOMInitCount == 0) + { + NativeEventQueue::uninit(); + hrc = NS_ShutdownXPCOM(nsnull); + + /* This is a thread initialized XPCOM and set gIsXPCOMInitialized to + * true. Reset it back to false. */ + bool wasInited = ASMAtomicXchgBool(&gIsXPCOMInitialized, false); + Assert(wasInited == true); + NOREF(wasInited); + } + } + } + +#endif /* !defined(VBOX_WITH_XPCOM) */ + + AssertComRC(hrc); + + return hrc; +} + +} /* namespace com */ diff --git a/src/VBox/Main/glue/string-base64.cpp b/src/VBox/Main/glue/string-base64.cpp new file mode 100644 index 00000000..7bad63ae --- /dev/null +++ b/src/VBox/Main/glue/string-base64.cpp @@ -0,0 +1,61 @@ +/* $Id: string-base64.cpp $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - UTF-8 and UTF-16 string classes, BASE64 bits. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "VBox/com/string.h" + +#include +#include + + +namespace com +{ + +HRESULT Bstr::base64Encode(const void *pvData, size_t cbData, bool fLineBreaks /*= false*/) +{ + uint32_t const fFlags = fLineBreaks ? RTBASE64_FLAGS_EOL_LF : RTBASE64_FLAGS_NO_LINE_BREAKS; + size_t cwcEncoded = RTBase64EncodedUtf16LengthEx(cbData, fFlags); + HRESULT hrc = reserveNoThrow(cwcEncoded + 1); + if (SUCCEEDED(hrc)) + { + int vrc = RTBase64EncodeUtf16Ex(pvData, cbData, fFlags, mutableRaw(), cwcEncoded + 1, &cwcEncoded); + AssertRCReturnStmt(vrc, setNull(), E_FAIL); + hrc = joltNoThrow(cwcEncoded); + } + return hrc; +} + +int Bstr::base64Decode(void *pvData, size_t cbData, size_t *pcbActual /*= NULL*/, PRTUTF16 *ppwszEnd /*= NULL*/) +{ + return RTBase64DecodeUtf16Ex(raw(), RTSTR_MAX, pvData, cbData, pcbActual, ppwszEnd); +} + +ssize_t Bstr::base64DecodedSize(PRTUTF16 *ppwszEnd /*= NULL*/) +{ + return RTBase64DecodedUtf16SizeEx(raw(), RTSTR_MAX, ppwszEnd); +} + +} /* namespace com */ diff --git a/src/VBox/Main/glue/string.cpp b/src/VBox/Main/glue/string.cpp new file mode 100644 index 00000000..177ffb46 --- /dev/null +++ b/src/VBox/Main/glue/string.cpp @@ -0,0 +1,1037 @@ +/* $Id: string.cpp $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - UTF-8 and UTF-16 string classes. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "VBox/com/string.h" + +#include +#include +#include +#include +#include + +namespace com +{ + +// BSTR representing a null wide char with 32 bits of length prefix (0); +// this will work on Windows as well as other platforms where BSTR does +// not use length prefixes +const OLECHAR g_achEmptyBstr[3] = { 0, 0, 0 }; +const BSTR g_bstrEmpty = (BSTR)&g_achEmptyBstr[2]; + +/* static */ +const Bstr Bstr::Empty; /* default ctor is OK */ + + +Bstr &Bstr::printf(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + HRESULT hrc = printfVNoThrow(pszFormat, va); + va_end(va); +#ifdef RT_EXCEPTIONS_ENABLED + if (hrc == S_OK) + { /* likely */ } + else + throw std::bad_alloc(); +#else + Assert(hrc == S_OK); RT_NOREF(hrc); +#endif + return *this; +} + +HRESULT Bstr::printfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT +{ + va_list va; + va_start(va, pszFormat); + HRESULT hrc = printfVNoThrow(pszFormat, va); + va_end(va); + return hrc; +} + + +Bstr &Bstr::printfV(const char *pszFormat, va_list va) +{ + HRESULT hrc = printfVNoThrow(pszFormat, va); +#ifdef RT_EXCEPTIONS_ENABLED + if (hrc == S_OK) + { /* likely */ } + else + throw std::bad_alloc(); +#else + Assert(hrc == S_OK); RT_NOREF(hrc); +#endif + return *this; +} + +struct BSTRNOTHROW +{ + Bstr *pThis; + size_t cwcAlloc; + size_t offDst; + HRESULT hrc; +}; + +/** + * Callback used with RTStrFormatV by Bstr::printfVNoThrow. + * + * @returns The number of bytes added (not used). + * + * @param pvArg Pointer to a BSTRNOTHROW structure. + * @param pachChars The characters to append. + * @param cbChars The number of characters. 0 on the final callback. + */ +/*static*/ DECLCALLBACK(size_t) +Bstr::printfOutputCallbackNoThrow(void *pvArg, const char *pachChars, size_t cbChars) RT_NOEXCEPT +{ + BSTRNOTHROW *pArgs = (BSTRNOTHROW *)pvArg; + if (cbChars) + { + size_t cwcAppend; + int vrc = ::RTStrCalcUtf16LenEx(pachChars, cbChars, &cwcAppend); + AssertRCReturnStmt(vrc, pArgs->hrc = E_UNEXPECTED, 0); + + /* + * Ensure we've got sufficient memory. + */ + Bstr *pThis = pArgs->pThis; + size_t const cwcBoth = pArgs->offDst + cwcAppend; + if (cwcBoth >= pArgs->cwcAlloc) + { + if (pArgs->hrc == S_OK) + { + /* Double the buffer size, if it's less that _1M. Align sizes like + for append. */ + size_t cwcAlloc = RT_ALIGN_Z(pArgs->cwcAlloc, 128); + cwcAlloc += RT_MIN(cwcAlloc, _1M); + if (cwcAlloc <= cwcBoth) + cwcAlloc = RT_ALIGN_Z(cwcBoth + 1, 512); + pArgs->hrc = pThis->reserveNoThrow(cwcAlloc, true /*fForce*/); + AssertMsgReturn(pArgs->hrc == S_OK, ("cwcAlloc=%#zx\n", cwcAlloc), 0); + pArgs->cwcAlloc = cwcAlloc; + } + else + return 0; + } + + /* + * Do the conversion. + */ + PRTUTF16 pwszDst = pThis->m_bstr + pArgs->offDst; + Assert(pArgs->cwcAlloc > pArgs->offDst); + vrc = ::RTStrToUtf16Ex(pachChars, cbChars, &pwszDst, pArgs->cwcAlloc - pArgs->offDst, &cwcAppend); + AssertRCReturnStmt(vrc, pArgs->hrc = E_UNEXPECTED, 0); + pArgs->offDst += cwcAppend; + } + return cbChars; +} + +HRESULT Bstr::printfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT +{ + cleanup(); + + BSTRNOTHROW Args = { this, 0, 0, S_OK }; + RTStrFormatV(printfOutputCallbackNoThrow, &Args, NULL, NULL, pszFormat, va); + if (Args.hrc == S_OK) + { + Args.hrc = joltNoThrow(Args.offDst); + if (Args.hrc == S_OK) + return S_OK; + } + + cleanup(); + return Args.hrc; +} + +void Bstr::copyFromN(const char *a_pszSrc, size_t a_cchMax) +{ + /* + * Initialize m_bstr first in case of throws further down in the code, then + * check for empty input (m_bstr == NULL means empty, there are no NULL + * strings). + */ + m_bstr = NULL; + if (!a_cchMax || !a_pszSrc || !*a_pszSrc) + return; + + /* + * Calculate the length and allocate a BSTR string buffer of the right + * size, i.e. optimize heap usage. + */ + size_t cwc; + int vrc = ::RTStrCalcUtf16LenEx(a_pszSrc, a_cchMax, &cwc); + if (RT_SUCCESS(vrc)) + { + m_bstr = ::SysAllocStringByteLen(NULL, (unsigned)(cwc * sizeof(OLECHAR))); + if (RT_LIKELY(m_bstr)) + { + PRTUTF16 pwsz = (PRTUTF16)m_bstr; + vrc = ::RTStrToUtf16Ex(a_pszSrc, a_cchMax, &pwsz, cwc + 1, NULL); + if (RT_SUCCESS(vrc)) + return; + + /* This should not happen! */ + AssertRC(vrc); + cleanup(); + } + } + else /* ASSUME: input is valid Utf-8. Fake out of memory error. */ + AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTStrNLen(a_pszSrc, a_cchMax), a_pszSrc)); +#ifdef RT_EXCEPTIONS_ENABLED + throw std::bad_alloc(); +#endif +} + +HRESULT Bstr::cleanupAndCopyFromNoThrow(const char *a_pszSrc, size_t a_cchMax) RT_NOEXCEPT +{ + /* + * Check for empty input (m_bstr == NULL means empty, there are no NULL strings). + */ + cleanup(); + if (!a_cchMax || !a_pszSrc || !*a_pszSrc) + return S_OK; + + /* + * Calculate the length and allocate a BSTR string buffer of the right + * size, i.e. optimize heap usage. + */ + HRESULT hrc; + size_t cwc; + int vrc = ::RTStrCalcUtf16LenEx(a_pszSrc, a_cchMax, &cwc); + if (RT_SUCCESS(vrc)) + { + m_bstr = ::SysAllocStringByteLen(NULL, (unsigned)(cwc * sizeof(OLECHAR))); + if (RT_LIKELY(m_bstr)) + { + PRTUTF16 pwsz = (PRTUTF16)m_bstr; + vrc = ::RTStrToUtf16Ex(a_pszSrc, a_cchMax, &pwsz, cwc + 1, NULL); + if (RT_SUCCESS(vrc)) + return S_OK; + + /* This should not happen! */ + AssertRC(vrc); + cleanup(); + hrc = E_UNEXPECTED; + } + else + hrc = E_OUTOFMEMORY; + } + else + { + /* Unexpected: Invalid UTF-8 input. */ + AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTStrNLen(a_pszSrc, a_cchMax), a_pszSrc)); + hrc = E_UNEXPECTED; + } + return hrc; +} + + +int Bstr::compareUtf8(const char *a_pszRight, CaseSensitivity a_enmCase /*= CaseSensitive*/) const +{ + PCRTUTF16 pwszLeft = m_bstr; + + /* + * Special case for null/empty strings. Unlike RTUtf16Cmp we + * treat null and empty equally. + */ + if (!pwszLeft) + return !a_pszRight || *a_pszRight == '\0' ? 0 : -1; + if (!a_pszRight) + return *pwszLeft == '\0' ? 0 : 1; + + /* + * Compare with a UTF-8 string by enumerating them char by char. + */ + for (;;) + { + RTUNICP ucLeft; + int vrc = RTUtf16GetCpEx(&pwszLeft, &ucLeft); + AssertRCReturn(vrc, 1); + + RTUNICP ucRight; + vrc = RTStrGetCpEx(&a_pszRight, &ucRight); + AssertRCReturn(vrc, -1); + if (ucLeft == ucRight) + { + if (ucLeft) + continue; + return 0; + } + + if (a_enmCase == CaseInsensitive) + { + if (RTUniCpToUpper(ucLeft) == RTUniCpToUpper(ucRight)) + continue; + if (RTUniCpToLower(ucLeft) == RTUniCpToLower(ucRight)) + continue; + } + + return ucLeft < ucRight ? -1 : 1; + } +} + + +bool Bstr::startsWith(Bstr const &a_rStart) const +{ + return RTUtf16NCmp(m_bstr, a_rStart.m_bstr, a_rStart.length()) == 0; +} + + +bool Bstr::startsWith(RTCString const &a_rStart) const +{ + return RTUtf16NCmpUtf8(m_bstr, a_rStart.c_str(), RTSTR_MAX, a_rStart.length()) == 0; +} + + +bool Bstr::startsWith(const char *a_pszStart) const +{ + return RTUtf16NCmpUtf8(m_bstr, a_pszStart, RTSTR_MAX, strlen(a_pszStart)) == 0; +} + + +#ifndef VBOX_WITH_XPCOM + +HRESULT Bstr::joltNoThrow(ssize_t cwcNew /* = -1*/) RT_NOEXCEPT +{ + if (m_bstr) + { + size_t const cwcAlloc = ::SysStringLen(m_bstr); + size_t const cwcActual = cwcNew < 0 ? ::RTUtf16Len(m_bstr) : (size_t)cwcNew; + Assert(cwcNew < 0 || cwcActual == ::RTUtf16Len(m_bstr)); + if (cwcActual != cwcAlloc) + { + Assert(cwcActual <= cwcAlloc); + Assert((unsigned int)cwcActual == cwcActual); + + /* Official way: Reallocate the string. We could of course just update the size-prefix if we dared... */ + if (!::SysReAllocStringLen(&m_bstr, NULL, (unsigned int)cwcActual)) + { + AssertFailed(); + return E_OUTOFMEMORY; + } + } + } + else + Assert(cwcNew <= 0); + return S_OK; +} + + +void Bstr::jolt(ssize_t cwcNew /* = -1*/) +{ + HRESULT hrc = joltNoThrow(cwcNew); +# ifdef RT_EXCEPTIONS_ENABLED + if (hrc != S_OK) + throw std::bad_alloc(); +# else + Assert(hrc == S_OK); RT_NOREF(hrc); +# endif +} + +#endif /* !VBOX_WITH_XPCOM */ + + +HRESULT Bstr::reserveNoThrow(size_t cwcMin, bool fForce /*= false*/) RT_NOEXCEPT +{ + /* If not forcing the string to the cwcMin length, check cwcMin against the + current string length: */ + if (!fForce) + { + size_t cwcCur = m_bstr ? ::SysStringLen(m_bstr) : 0; + if (cwcCur >= cwcMin) + return S_OK; + } + + /* The documentation for SysReAllocStringLen hints about it being allergic + to NULL in some way or another, so we call SysAllocStringLen directly + when appropriate: */ + if (m_bstr) + AssertReturn(::SysReAllocStringLen(&m_bstr, NULL, (unsigned int)cwcMin) != FALSE, E_OUTOFMEMORY); + else if (cwcMin > 0) + { + m_bstr = ::SysAllocStringLen(NULL, (unsigned int)cwcMin); + AssertReturn(m_bstr, E_OUTOFMEMORY); + } + + return S_OK; +} + + +void Bstr::reserve(size_t cwcMin, bool fForce /*= false*/) +{ + HRESULT hrc = reserveNoThrow(cwcMin, fForce); +#ifdef RT_EXCEPTIONS_ENABLED + if (hrc != S_OK) + throw std::bad_alloc(); +#else + Assert(hrc == S_OK); RT_NOREF(hrc); +#endif +} + + +Bstr &Bstr::append(const Bstr &rThat) +{ + if (rThat.isNotEmpty()) + return appendWorkerUtf16(rThat.m_bstr, rThat.length()); + return *this; +} + + +HRESULT Bstr::appendNoThrow(const Bstr &rThat) RT_NOEXCEPT +{ + if (rThat.isNotEmpty()) + return appendWorkerUtf16NoThrow(rThat.m_bstr, rThat.length()); + return S_OK; +} + + +Bstr &Bstr::append(const RTCString &rThat) +{ + if (rThat.isNotEmpty()) + return appendWorkerUtf8(rThat.c_str(), rThat.length()); + return *this; +} + + +HRESULT Bstr::appendNoThrow(const RTCString &rThat) RT_NOEXCEPT +{ + if (rThat.isNotEmpty()) + return appendWorkerUtf8NoThrow(rThat.c_str(), rThat.length()); + return S_OK; +} + + +Bstr &Bstr::append(CBSTR pwszSrc) +{ + if (pwszSrc && *pwszSrc) + return appendWorkerUtf16(pwszSrc, RTUtf16Len(pwszSrc)); + return *this; +} + + +HRESULT Bstr::appendNoThrow(CBSTR pwszSrc) RT_NOEXCEPT +{ + if (pwszSrc && *pwszSrc) + return appendWorkerUtf16NoThrow(pwszSrc, RTUtf16Len(pwszSrc)); + return S_OK; +} + + +Bstr &Bstr::append(const char *pszSrc) +{ + if (pszSrc && *pszSrc) + return appendWorkerUtf8(pszSrc, strlen(pszSrc)); + return *this; +} + + +HRESULT Bstr::appendNoThrow(const char *pszSrc) RT_NOEXCEPT +{ + if (pszSrc && *pszSrc) + return appendWorkerUtf8NoThrow(pszSrc, strlen(pszSrc)); + return S_OK; +} + + +Bstr &Bstr::append(const Bstr &rThat, size_t offStart, size_t cwcMax /*= RTSTR_MAX*/) +{ + size_t cwcSrc = rThat.length(); + if (offStart < cwcSrc) + return appendWorkerUtf16(rThat.raw() + offStart, RT_MIN(cwcSrc - offStart, cwcMax)); + return *this; +} + + +HRESULT Bstr::appendNoThrow(const Bstr &rThat, size_t offStart, size_t cwcMax /*= RTSTR_MAX*/) RT_NOEXCEPT +{ + size_t cwcSrc = rThat.length(); + if (offStart < cwcSrc) + return appendWorkerUtf16NoThrow(rThat.raw() + offStart, RT_MIN(cwcSrc - offStart, cwcMax)); + return S_OK; +} + + +Bstr &Bstr::append(const RTCString &rThat, size_t offStart, size_t cchMax /*= RTSTR_MAX*/) +{ + if (offStart < rThat.length()) + return appendWorkerUtf8(rThat.c_str() + offStart, RT_MIN(rThat.length() - offStart, cchMax)); + return *this; +} + + +HRESULT Bstr::appendNoThrow(const RTCString &rThat, size_t offStart, size_t cchMax /*= RTSTR_MAX*/) RT_NOEXCEPT +{ + if (offStart < rThat.length()) + return appendWorkerUtf8NoThrow(rThat.c_str() + offStart, RT_MIN(rThat.length() - offStart, cchMax)); + return S_OK; +} + + +Bstr &Bstr::append(CBSTR pwszThat, size_t cchMax) +{ + return appendWorkerUtf16(pwszThat, RTUtf16NLen(pwszThat, cchMax)); +} + + +HRESULT Bstr::appendNoThrow(CBSTR pwszThat, size_t cchMax) RT_NOEXCEPT +{ + return appendWorkerUtf16NoThrow(pwszThat, RTUtf16NLen(pwszThat, cchMax)); +} + + +Bstr &Bstr::append(const char *pszThat, size_t cchMax) +{ + return appendWorkerUtf8(pszThat, RTStrNLen(pszThat, cchMax)); +} + + +HRESULT Bstr::appendNoThrow(const char *pszThat, size_t cchMax) RT_NOEXCEPT +{ + return appendWorkerUtf8NoThrow(pszThat, RTStrNLen(pszThat, cchMax)); +} + + +Bstr &Bstr::append(char ch) +{ + AssertMsg(ch > 0 && ch < 127, ("%#x\n", ch)); + return appendWorkerUtf8(&ch, 1); +} + + +HRESULT Bstr::appendNoThrow(char ch) RT_NOEXCEPT +{ + AssertMsg(ch > 0 && ch < 127, ("%#x\n", ch)); + return appendWorkerUtf8NoThrow(&ch, 1); +} + + +Bstr &Bstr::appendCodePoint(RTUNICP uc) +{ + RTUTF16 wszTmp[3]; + PRTUTF16 pwszEnd = RTUtf16PutCp(wszTmp, uc); + *pwszEnd = '\0'; + return appendWorkerUtf16(&wszTmp[0], pwszEnd - &wszTmp[0]); +} + + +HRESULT Bstr::appendCodePointNoThrow(RTUNICP uc) RT_NOEXCEPT +{ + RTUTF16 wszTmp[3]; + PRTUTF16 pwszEnd = RTUtf16PutCp(wszTmp, uc); + *pwszEnd = '\0'; + return appendWorkerUtf16NoThrow(&wszTmp[0], pwszEnd - &wszTmp[0]); +} + + +Bstr &Bstr::appendWorkerUtf16(PCRTUTF16 pwszSrc, size_t cwcSrc) +{ + size_t cwcOld = length(); + size_t cwcTotal = cwcOld + cwcSrc; + reserve(cwcTotal, true /*fForce*/); + if (cwcSrc) + memcpy(&m_bstr[cwcOld], pwszSrc, cwcSrc * sizeof(RTUTF16)); + m_bstr[cwcTotal] = '\0'; + return *this; +} + + +HRESULT Bstr::appendWorkerUtf16NoThrow(PCRTUTF16 pwszSrc, size_t cwcSrc) RT_NOEXCEPT +{ + size_t cwcOld = length(); + size_t cwcTotal = cwcOld + cwcSrc; + HRESULT hrc = reserveNoThrow(cwcTotal, true /*fForce*/); + if (hrc == S_OK) + { + if (cwcSrc) + memcpy(&m_bstr[cwcOld], pwszSrc, cwcSrc * sizeof(RTUTF16)); + m_bstr[cwcTotal] = '\0'; + } + return hrc; +} + + +Bstr &Bstr::appendWorkerUtf8(const char *pszSrc, size_t cchSrc) +{ + size_t cwcSrc; + int vrc = RTStrCalcUtf16LenEx(pszSrc, cchSrc, &cwcSrc); +#ifdef RT_EXCEPTIONS_ENABLED + AssertRCStmt(vrc, throw std::bad_alloc()); +#else + AssertRCReturn(vrc, *this); +#endif + + size_t cwcOld = length(); + size_t cwcTotal = cwcOld + cwcSrc; + reserve(cwcTotal, true /*fForce*/); + if (cwcSrc) + { + PRTUTF16 pwszDst = &m_bstr[cwcOld]; + vrc = RTStrToUtf16Ex(pszSrc, cchSrc, &pwszDst, cwcSrc + 1, NULL); +#ifdef RT_EXCEPTIONS_ENABLED + AssertRCStmt(vrc, throw std::bad_alloc()); +#else + AssertRC(vrc); +#endif + } + m_bstr[cwcTotal] = '\0'; + return *this; +} + + +HRESULT Bstr::appendWorkerUtf8NoThrow(const char *pszSrc, size_t cchSrc) RT_NOEXCEPT +{ + size_t cwcSrc; + int vrc = RTStrCalcUtf16LenEx(pszSrc, cchSrc, &cwcSrc); + AssertRCStmt(vrc, E_INVALIDARG); + + size_t cwcOld = length(); + size_t cwcTotal = cwcOld + cwcSrc; + HRESULT hrc = reserveNoThrow(cwcTotal, true /*fForce*/); + AssertReturn(hrc == S_OK, hrc); + if (cwcSrc) + { + PRTUTF16 pwszDst = &m_bstr[cwcOld]; + vrc = RTStrToUtf16Ex(pszSrc, cchSrc, &pwszDst, cwcSrc + 1, NULL); + AssertRCStmt(vrc, E_INVALIDARG); + } + m_bstr[cwcTotal] = '\0'; + return S_OK; +} + + +Bstr &Bstr::appendPrintf(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + HRESULT hrc = appendPrintfVNoThrow(pszFormat, va); + va_end(va); +#ifdef RT_EXCEPTIONS_ENABLED + if (hrc != S_OK) + throw std::bad_alloc(); +#else + Assert(hrc == S_OK); RT_NOREF(hrc); +#endif + return *this; +} + + +HRESULT Bstr::appendPrintfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT +{ + va_list va; + va_start(va, pszFormat); + HRESULT hrc = appendPrintfVNoThrow(pszFormat, va); + va_end(va); + return hrc; +} + + +Bstr &Bstr::appendPrintfV(const char *pszFormat, va_list va) +{ + HRESULT hrc = appendPrintfVNoThrow(pszFormat, va); +#ifdef RT_EXCEPTIONS_ENABLED + if (hrc != S_OK) + throw std::bad_alloc(); +#else + Assert(hrc == S_OK); RT_NOREF(hrc); +#endif + return *this; +} + + +HRESULT Bstr::appendPrintfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT +{ + size_t const cwcOld = length(); + BSTRNOTHROW Args = { this, cwcOld, cwcOld, S_OK }; + + RTStrFormatV(printfOutputCallbackNoThrow, &Args, NULL, NULL, pszFormat, va); + if (Args.hrc == S_OK) + { + Args.hrc = joltNoThrow(Args.offDst); + if (Args.hrc == S_OK) + return S_OK; + } + + if (m_bstr) + m_bstr[cwcOld] = '\0'; + return Args.hrc; +} + + +Bstr &Bstr::erase(size_t offStart /*= 0*/, size_t cwcLength /*= RTSTR_MAX*/) RT_NOEXCEPT +{ + size_t cwc = length(); + if (offStart < cwc) + { + if (cwcLength >= cwc - offStart) + { + if (!offStart) + cleanup(); + else + { + /* Trail removal, nothing to move. */ + m_bstr[offStart] = '\0'; + joltNoThrow(offStart); /* not entirely optimal... */ + } + } + else if (cwcLength > 0) + { + /* Pull up the tail to offStart. */ + size_t cwcAfter = cwc - offStart - cwcLength; + memmove(&m_bstr[offStart], &m_bstr[offStart + cwcLength], cwcAfter * sizeof(*m_bstr)); + cwc -= cwcLength; + m_bstr[cwc] = '\0'; + joltNoThrow(cwc); /* not entirely optimal... */ + } + } + return *this; +} + + +void Bstr::cleanup() +{ + if (m_bstr) + { + ::SysFreeString(m_bstr); + m_bstr = NULL; + } +} + + +void Bstr::copyFrom(const OLECHAR *a_bstrSrc) +{ + if (a_bstrSrc && *a_bstrSrc) + { + m_bstr = ::SysAllocString(a_bstrSrc); +#ifdef RT_EXCEPTIONS_ENABLED + if (RT_LIKELY(m_bstr)) + { /* likely */ } + else + throw std::bad_alloc(); +#else + Assert(m_bstr); +#endif + } + else + m_bstr = NULL; +} + + +void Bstr::cleanupAndCopyFrom(const OLECHAR *a_bstrSrc) +{ + cleanup(); + copyFrom(a_bstrSrc); +} + + +HRESULT Bstr::cleanupAndCopyFromEx(const OLECHAR *a_bstrSrc) RT_NOEXCEPT +{ + cleanup(); + + if (a_bstrSrc && *a_bstrSrc) + { + m_bstr = ::SysAllocString(a_bstrSrc); + if (RT_LIKELY(m_bstr)) + { /* likely */ } + else + return E_OUTOFMEMORY; + } + else + m_bstr = NULL; + return S_OK; +} + + + +/********************************************************************************************************************************* +* Utf8Str Implementation * +*********************************************************************************************************************************/ + +/* static */ +const Utf8Str Utf8Str::Empty; /* default ctor is OK */ + +#if defined(VBOX_WITH_XPCOM) +void Utf8Str::cloneTo(char **pstr) const +{ + size_t cb = length() + 1; + *pstr = (char *)nsMemory::Alloc(cb); + if (RT_LIKELY(*pstr)) + memcpy(*pstr, c_str(), cb); + else +#ifdef RT_EXCEPTIONS_ENABLED + throw std::bad_alloc(); +#else + AssertFailed(); +#endif +} + +HRESULT Utf8Str::cloneToEx(char **pstr) const +{ + size_t cb = length() + 1; + *pstr = (char *)nsMemory::Alloc(cb); + if (RT_LIKELY(*pstr)) + { + memcpy(*pstr, c_str(), cb); + return S_OK; + } + return E_OUTOFMEMORY; +} +#endif + +HRESULT Utf8Str::cloneToEx(BSTR *pbstr) const RT_NOEXCEPT +{ + if (!pbstr) + return S_OK; + Bstr bstr; + HRESULT hrc = bstr.assignEx(*this); + if (SUCCEEDED(hrc)) + hrc = bstr.detachToEx(pbstr); + return hrc; +} + +Utf8Str& Utf8Str::stripTrailingSlash() +{ + if (length()) + { + ::RTPathStripTrailingSlash(m_psz); + jolt(); + } + return *this; +} + +Utf8Str& Utf8Str::stripFilename() +{ + if (length()) + { + RTPathStripFilename(m_psz); + jolt(); + } + return *this; +} + +Utf8Str& Utf8Str::stripPath() +{ + if (length()) + { + char *pszName = ::RTPathFilename(m_psz); + if (pszName) + { + size_t cchName = length() - (pszName - m_psz); + memmove(m_psz, pszName, cchName + 1); + jolt(); + } + else + cleanup(); + } + return *this; +} + +Utf8Str& Utf8Str::stripSuffix() +{ + if (length()) + { + RTPathStripSuffix(m_psz); + jolt(); + } + return *this; +} + +size_t Utf8Str::parseKeyValue(Utf8Str &a_rKey, Utf8Str &a_rValue, size_t a_offStart /* = 0*/, + const Utf8Str &a_rPairSeparator /*= ","*/, const Utf8Str &a_rKeyValueSeparator /*= "="*/) const +{ + /* Find the end of the next pair, skipping empty pairs. + Note! The skipping allows us to pass the return value of a parseKeyValue() + call as offStart to the next call. */ + size_t offEnd; + while ( a_offStart == (offEnd = find(&a_rPairSeparator, a_offStart)) + && offEnd != npos) + a_offStart++; + + /* Look for a key/value separator before the end of the pair. + ASSUMES npos value returned by find when the substring is not found is + really high. */ + size_t offKeyValueSep = find(&a_rKeyValueSeparator, a_offStart); + if (offKeyValueSep < offEnd) + { + a_rKey = substr(a_offStart, offKeyValueSep - a_offStart); + if (offEnd == npos) + offEnd = m_cch; /* No confusing npos when returning strings. */ + a_rValue = substr(offKeyValueSep + 1, offEnd - offKeyValueSep - 1); + } + else + { + a_rKey.setNull(); + a_rValue.setNull(); + } + + return offEnd; +} + +/** + * Internal function used in Utf8Str copy constructors and assignment when + * copying from a UTF-16 string. + * + * As with the RTCString::copyFrom() variants, this unconditionally sets the + * members to a copy of the given other strings and makes no assumptions about + * previous contents. This can therefore be used both in copy constructors, + * when member variables have no defined value, and in assignments after having + * called cleanup(). + * + * This variant converts from a UTF-16 string, most probably from + * a Bstr assignment. + * + * @param a_pbstr The source string. The caller guarantees that this + * is valid UTF-16. + * @param a_cwcMax The number of characters to be copied. If set to RTSTR_MAX, + * the entire string will be copied. + * + * @sa RTCString::copyFromN + */ +void Utf8Str::copyFrom(CBSTR a_pbstr, size_t a_cwcMax) +{ + if (a_pbstr && *a_pbstr) + { + int vrc = RTUtf16ToUtf8Ex((PCRTUTF16)a_pbstr, + a_cwcMax, // size_t cwcString: translate entire string + &m_psz, // char **ppsz: output buffer + 0, // size_t cch: if 0, func allocates buffer in *ppsz + &m_cch); // size_t *pcch: receives the size of the output string, excluding the terminator. + if (RT_SUCCESS(vrc)) + m_cbAllocated = m_cch + 1; + else + { + if ( vrc != VERR_NO_STR_MEMORY + && vrc != VERR_NO_MEMORY) + { + /* ASSUME: input is valid Utf-16. Fake out of memory error. */ + AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTUtf16Len(a_pbstr) * sizeof(RTUTF16), a_pbstr)); + } + + m_cch = 0; + m_cbAllocated = 0; + m_psz = NULL; + +#ifdef RT_EXCEPTIONS_ENABLED + throw std::bad_alloc(); +#else + AssertFailed(); +#endif + } + } + else + { + m_cch = 0; + m_cbAllocated = 0; + m_psz = NULL; + } +} + +/** + * A variant of Utf8Str::copyFrom that does not throw any exceptions but returns + * E_OUTOFMEMORY instead. + * + * @param a_pbstr The source string. + * @returns S_OK or E_OUTOFMEMORY. + */ +HRESULT Utf8Str::copyFromEx(CBSTR a_pbstr) +{ + if (a_pbstr && *a_pbstr) + { + int vrc = RTUtf16ToUtf8Ex((PCRTUTF16)a_pbstr, + RTSTR_MAX, // size_t cwcString: translate entire string + &m_psz, // char **ppsz: output buffer + 0, // size_t cch: if 0, func allocates buffer in *ppsz + &m_cch); // size_t *pcch: receives the size of the output string, excluding the terminator. + if (RT_SUCCESS(vrc)) + m_cbAllocated = m_cch + 1; + else + { + if ( vrc != VERR_NO_STR_MEMORY + && vrc != VERR_NO_MEMORY) + { + /* ASSUME: input is valid Utf-16. Fake out of memory error. */ + AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTUtf16Len(a_pbstr) * sizeof(RTUTF16), a_pbstr)); + } + + m_cch = 0; + m_cbAllocated = 0; + m_psz = NULL; + + return E_OUTOFMEMORY; + } + } + else + { + m_cch = 0; + m_cbAllocated = 0; + m_psz = NULL; + } + return S_OK; +} + + +/** + * A variant of Utf8Str::copyFromN that does not throw any exceptions but + * returns E_OUTOFMEMORY instead. + * + * @param a_pcszSrc The source string. + * @param a_offSrc Start offset to copy from. + * @param a_cchSrc How much to copy + * @returns S_OK or E_OUTOFMEMORY. + * + * @remarks This calls cleanup() first, so the caller doesn't have to. (Saves + * code space.) + */ +HRESULT Utf8Str::copyFromExNComRC(const char *a_pcszSrc, size_t a_offSrc, size_t a_cchSrc) +{ + Assert(!a_cchSrc || !m_psz || (uintptr_t)&a_pcszSrc[a_offSrc] - (uintptr_t)m_psz >= (uintptr_t)m_cbAllocated); + cleanup(); + if (a_cchSrc) + { + m_psz = RTStrAlloc(a_cchSrc + 1); + if (RT_LIKELY(m_psz)) + { + m_cch = a_cchSrc; + m_cbAllocated = a_cchSrc + 1; + memcpy(m_psz, a_pcszSrc + a_offSrc, a_cchSrc); + m_psz[a_cchSrc] = '\0'; + } + else + { + m_cch = 0; + m_cbAllocated = 0; + return E_OUTOFMEMORY; + } + } + else + { + m_cch = 0; + m_cbAllocated = 0; + m_psz = NULL; + } + return S_OK; +} + +} /* namespace com */ diff --git a/src/VBox/Main/glue/tests/Makefile b/src/VBox/Main/glue/tests/Makefile new file mode 100644 index 00000000..84d670f1 --- /dev/null +++ b/src/VBox/Main/glue/tests/Makefile @@ -0,0 +1,81 @@ +## @file +# Makefile for a sample/testcase using the 'glue' Java API bindings + +# +# Copyright (C) 2010-2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# SPDX-License-Identifier: GPL-3.0-only +# + +# +# User serviceable parts: adjust the following lines appropriately +# + +# The host OS: linux, solaris, win, darwin, freebsd +HOSTOS=linux +# Absolute (!) path to the VirtualBox install directory +VBOX_BIN = /opt/VirtualBox +# Path to the sdk directory of the unpacked VirtualBox SDK +VBOX_SDK = ../../.. +# On Windows, if you want to use the COM API: install directory of Jacob lib +JACOB_DIR = +# Extra classpath entries needed for compiling/running the sample +CLASSPATH = +# Java compiler to use +JAVAC = javac +# Java VM to use +JAVA = java + + +# +# No changes should be necessary after this point. +# + +ifeq ($(HOSTOS),win) + JACOB_JAR=$(JACOB_DIR)/jacob.jar + VBOX_JAR=$(VBOX_SDK)/bindings/mscom/java/vboxjmscom.jar + SEP=\; + JAVA_COM_ARGS += -Djava.library.path=$(JACOB_DIR) + CLASSPATH += $(JACOB_JAR)$(SEP) +else + VBOX_JAR=$(VBOX_SDK)/bindings/xpcom/java/vboxjxpcom.jar + SEP=: + JAVA_COM_ARGS += -Dvbox.home=$(VBOX_BIN) +endif + +VBOX_JAR_WS=$(VBOX_SDK)/bindings/webservice/java/jax-ws/vboxjws.jar + + +all: ws/TestVBox.class com/TestVBox.class + +test: ws/test-TestVBox com/test-TestVBox + +com/TestVBox.class: + @mkdir com 2>/dev/null || true + $(JAVAC) -d com -cp $(VBOX_JAR)$(SEP)$(CLASSPATH) TestVBox.java + +com/test-TestVBox: com/TestVBox.class + $(JAVA) $(JAVA_COM_ARGS) -cp com$(SEP)$(VBOX_JAR)$(SEP)$(CLASSPATH) TestVBox + +ws/TestVBox.class: + @mkdir ws 2>/dev/null || true + $(JAVAC) -d ws -cp $(VBOX_JAR_WS)$(SEP)$(CLASSPATH) TestVBox.java + +ws/test-TestVBox: ws/TestVBox.class + $(JAVA) $(JAVA_WS_ARGS) -cp ws$(SEP)$(VBOX_JAR_WS)$(SEP)$(CLASSPATH) TestVBox -w -url http://localhost:18083/ diff --git a/src/VBox/Main/glue/tests/TestVBox.java b/src/VBox/Main/glue/tests/TestVBox.java new file mode 100644 index 00000000..37bce704 --- /dev/null +++ b/src/VBox/Main/glue/tests/TestVBox.java @@ -0,0 +1,310 @@ +/* $Id: TestVBox.java $ */ +/*! file + * Small sample/testcase which demonstrates that the same source code can + * be used to connect to the webservice and (XP)COM APIs. + */ + +/* + * Copyright (C) 2010-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +import org.virtualbox_6_2.*; +import java.util.List; +import java.util.Arrays; +import java.util.ArrayList; +import java.math.BigInteger; + +public class TestVBox +{ + static void processEvent(IEvent ev) + { + System.out.println("got event: " + ev); + VBoxEventType type = ev.getType(); + System.out.println("type = " + type); + switch (type) + { + case OnMachineStateChanged: + { + IMachineStateChangedEvent mcse = IMachineStateChangedEvent.queryInterface(ev); + if (mcse == null) + System.out.println("Cannot query an interface"); + else + System.out.println("mid=" + mcse.getMachineId()); + break; + } + } + } + + static class EventHandler + { + EventHandler() {} + public void handleEvent(IEvent ev) + { + try { + processEvent(ev); + } catch (Throwable t) { + t.printStackTrace(); + } + } + } + + static void testEvents(VirtualBoxManager mgr, IEventSource es) + { + // active mode for Java doesn't fully work yet, and using passive + // is more portable (the only mode for MSCOM and WS) and thus generally + // recommended + IEventListener listener = es.createListener(); + + es.registerListener(listener, Arrays.asList(VBoxEventType.Any), false); + + try { + for (int i = 0; i < 50; i++) + { + System.out.print("."); + IEvent ev = es.getEvent(listener, 500); + if (ev != null) + { + processEvent(ev); + es.eventProcessed(listener, ev); + } + // process system event queue + mgr.waitForEvents(0); + } + } catch (Exception e) { + e.printStackTrace(); + } + + es.unregisterListener(listener); + } + + static void testEnumeration(VirtualBoxManager mgr, IVirtualBox vbox) + { + List machs = vbox.getMachines(); + for (IMachine m : machs) + { + String name; + Long ram = 0L; + boolean hwvirtEnabled = false, hwvirtNestedPaging = false; + boolean paeEnabled = false; + boolean inaccessible = false; + try + { + name = m.getName(); + ram = m.getMemorySize(); + hwvirtEnabled = m.getHWVirtExProperty(HWVirtExPropertyType.Enabled); + hwvirtNestedPaging = m.getHWVirtExProperty(HWVirtExPropertyType.NestedPaging); + paeEnabled = m.getCPUProperty(CPUPropertyType.PAE); + String osType = m.getOSTypeId(); + IGuestOSType foo = vbox.getGuestOSType(osType); + } + catch (VBoxException e) + { + name = ""; + inaccessible = true; + } + System.out.println("VM name: " + name); + if (!inaccessible) + { + System.out.println(" RAM size: " + ram + "MB" + + ", HWVirt: " + hwvirtEnabled + + ", Nested Paging: " + hwvirtNestedPaging + + ", PAE: " + paeEnabled); + } + } + // process system event queue + mgr.waitForEvents(0); + } + + static boolean progressBar(VirtualBoxManager mgr, IProgress p, long waitMillis) + { + long end = System.currentTimeMillis() + waitMillis; + while (!p.getCompleted()) + { + // process system event queue + mgr.waitForEvents(0); + // wait for completion of the task, but at most 200 msecs + p.waitForCompletion(200); + if (System.currentTimeMillis() >= end) + return false; + } + return true; + } + + static void testStart(VirtualBoxManager mgr, IVirtualBox vbox) + { + IMachine m = vbox.getMachines().get(0); + String name = m.getName(); + System.out.println("\nAttempting to start VM '" + name + "'"); + + ISession session = mgr.getSessionObject(); + ArrayList env = new ArrayList(); + IProgress p = m.launchVMProcess(session, "gui", env); + progressBar(mgr, p, 10000); + session.unlockMachine(); + // process system event queue + mgr.waitForEvents(0); + } + + static void testMultiServer() + { + VirtualBoxManager mgr1 = VirtualBoxManager.createInstance(null); + VirtualBoxManager mgr2 = VirtualBoxManager.createInstance(null); + + try { + mgr1.connect("http://i7:18083", "", ""); + mgr2.connect("http://main:18083", "", ""); + + IMachine m1 = mgr1.getVBox().getMachines().get(0); + IMachine m2 = mgr2.getVBox().getMachines().get(0); + String name1 = m1.getName(); + String name2 = m2.getName(); + ISession session1 = mgr1.getSessionObject(); + ISession session2 = mgr2.getSessionObject(); + ArrayList env = new ArrayList(); + IProgress p1 = m1.launchVMProcess(session1, "gui", env); + IProgress p2 = m2.launchVMProcess(session2, "gui", env); + progressBar(mgr1, p1, 10000); + progressBar(mgr2, p2, 10000); + session1.unlockMachine(); + session2.unlockMachine(); + // process system event queue + mgr1.waitForEvents(0); + mgr2.waitForEvents(0); + } finally { + mgr1.cleanup(); + mgr2.cleanup(); + } + } + + static void testReadLog(VirtualBoxManager mgr, IVirtualBox vbox) + { + IMachine m = vbox.getMachines().get(0); + long logNo = 0; + long off = 0; + long size = 16 * 1024; + while (true) + { + byte[] buf = m.readLog(logNo, off, size); + if (buf.length == 0) + break; + System.out.print(new String(buf)); + off += buf.length; + } + // process system event queue + mgr.waitForEvents(0); + } + + static void printErrorInfo(VBoxException e) + { + System.out.println("VBox error: " + e.getMessage()); + System.out.println("Error cause message: " + e.getCause()); + System.out.println("Overall result code: " + Integer.toHexString(e.getResultCode())); + int i = 1; + for (IVirtualBoxErrorInfo ei = e.getVirtualBoxErrorInfo(); ei != null; ei = ei.getNext(), i++) + { + System.out.println("Detail information #" + i); + System.out.println("Error mesage: " + ei.getText()); + System.out.println("Result code: " + Integer.toHexString(ei.getResultCode())); + // optional, usually provides little additional information: + System.out.println("Component: " + ei.getComponent()); + System.out.println("Interface ID: " + ei.getInterfaceID()); + } + } + + + public static void main(String[] args) + { + VirtualBoxManager mgr = VirtualBoxManager.createInstance(null); + + boolean ws = false; + String url = null; + String user = null; + String passwd = null; + + for (int i = 0; i < args.length; i++) + { + if (args[i].equals("-w")) + ws = true; + else if (args[i].equals("-url")) + url = args[++i]; + else if (args[i].equals("-user")) + user = args[++i]; + else if (args[i].equals("-passwd")) + passwd = args[++i]; + } + + if (ws) + { + try { + mgr.connect(url, user, passwd); + } catch (VBoxException e) { + e.printStackTrace(); + System.out.println("Cannot connect, start webserver first!"); + } + } + + try + { + IVirtualBox vbox = mgr.getVBox(); + if (vbox != null) + { + System.out.println("VirtualBox version: " + vbox.getVersion() + "\n"); + testEnumeration(mgr, vbox); + testReadLog(mgr, vbox); + testStart(mgr, vbox); + testEvents(mgr, vbox.getEventSource()); + + System.out.println("done, press Enter..."); + int ch = System.in.read(); + } + } + catch (VBoxException e) + { + printErrorInfo(e); + System.out.println("Java stack trace:"); + e.printStackTrace(); + } + catch (RuntimeException e) + { + System.out.println("Runtime error: " + e.getMessage()); + e.printStackTrace(); + } + catch (java.io.IOException e) + { + e.printStackTrace(); + } + + // process system event queue + mgr.waitForEvents(0); + if (ws) + { + try { + mgr.disconnect(); + } catch (VBoxException e) { + e.printStackTrace(); + } + } + + mgr.cleanup(); + + } + +} diff --git a/src/VBox/Main/glue/tests/TestVBoxNATEngine.java b/src/VBox/Main/glue/tests/TestVBoxNATEngine.java new file mode 100644 index 00000000..a54f163c --- /dev/null +++ b/src/VBox/Main/glue/tests/TestVBoxNATEngine.java @@ -0,0 +1,206 @@ +/* $Id: TestVBoxNATEngine.java $ */ +/*!file + * Small sample/testcase which demonstrates that the same source code can + * be used to connect to the webservice and (XP)COM APIs. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +import org.virtualbox_5_0.*; +import java.util.List; +import java.util.Arrays; +import java.util.Iterator; +import java.math.BigInteger; + +public class TestVBoxNATEngine +{ + void testEnumeration(VirtualBoxManager mgr, IVirtualBox vbox, IMachine vm) + { + String name; + boolean inaccessible = false; + /* different chipsets might have different number of attachments */ + ChipsetType chipsetType = vm.getChipsetType(); + INetworkAdapter adapters[] = + new INetworkAdapter[ + vbox.getSystemProperties().getMaxNetworkAdapters(chipsetType).intValue()]; + + try + { + name = vm.getName(); + /* + * Dump adapters and if it's got NAT attachment + * dump it settings + */ + + for (int nidx = 0; nidx < adapters.length; ++nidx) + { + /* select available and NATs only. */ + adapters[nidx] = vm.getNetworkAdapter(new Long(nidx)); + INetworkAdapter n = adapters[nidx]; + + if (n == null) + continue; + NetworkAttachmentType attachmentType = n.getAttachmentType(); + if (attachmentType.equals(NetworkAttachmentType.NAT)) + { + INATEngine nat = n.getNATEngine(); + List portForward = nat.getRedirects(); + String pf = null; + Iterator itPortForward = portForward.iterator(); + for (;itPortForward.hasNext();) + { + pf = itPortForward.next(); + System.out.println(name + ":NIC" + n.getSlot() /* name:NIC*/ + + " pf: " + pf); /* port-forward rule */ + } + if (pf != null) + { + String pfAttributes[] = pf.split(","); + /* name,proto,hostip,host,hostport,guestip,guestport */ + nat.removeRedirect(pfAttributes[0]); + nat.addRedirect("", + NATProtocol.fromValue(new Integer(pfAttributes[1]).longValue()), + pfAttributes[2], + new Integer( + new Integer(pfAttributes[3]).intValue() + 1), + pfAttributes[4], + new Integer(pfAttributes[5])); + } + + } + } + + } + catch (VBoxException e) + { + name = ""; + inaccessible = true; + } + + // process system event queue + mgr.waitForEvents(0); + } + + static void testStart(VirtualBoxManager mgr, IVirtualBox vbox, IMachine vm) + { + System.out.println("\nAttempting to start VM '" + vm.getName() + "'"); + mgr.startVm(vm.getName(), null, 7000); + // process system event queue + mgr.waitForEvents(0); + } + + public TestVBoxNATEngine(String[] args) + { + VirtualBoxManager mgr = VirtualBoxManager.createInstance(null); + + boolean ws = false; + String url = null; + String user = null; + String passwd = null; + String vmname = null; + IMachine vm = null; + + for (int i = 0; i. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL), a copy of it is provided in the "COPYING.CDDL" file included +in the VirtualBox distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. + +SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +""" +__version__ = "$Revision: 155244 $" + + +# Note! To set Python bitness on OSX use 'export VERSIONER_PYTHON_PREFER_32_BIT=yes' + + +# Standard Python imports. +import os +import sys +import traceback + + +if sys.version_info >= (3, 0): + xrange = range + long = int + +# +# Globals, environment and sys.path changes. +# +import platform +VBoxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None) +VBoxSdkDir = os.environ.get("VBOX_SDK_PATH", None) + +if VBoxBinDir is None: + if platform.system() == 'Darwin': + VBoxBinDir = '/Applications/VirtualBox.app/Contents/MacOS' + else: # Will be set by the installer + VBoxBinDir = "%VBOX_INSTALL_PATH%" +else: + VBoxBinDir = os.path.abspath(VBoxBinDir) + +if VBoxSdkDir is None: + if platform.system() == 'Darwin': + VBoxSdkDir = '/Applications/VirtualBox.app/Contents/MacOS/sdk' + else: # Will be set by the installer + VBoxSdkDir = "%VBOX_SDK_PATH%" +else: + VBoxSdkDir = os.path.abspath(VBoxSdkDir) + +os.environ["VBOX_PROGRAM_PATH"] = VBoxBinDir +os.environ["VBOX_SDK_PATH"] = VBoxSdkDir +sys.path.append(VBoxBinDir) + + +# +# Import the generated VirtualBox constants. +# +from .VirtualBox_constants import VirtualBoxReflectionInfo + + +class PerfCollector(object): + """ This class provides a wrapper over IPerformanceCollector in order to + get more 'pythonic' interface. + + To begin collection of metrics use setup() method. + + To get collected data use query() method. + + It is possible to disable metric collection without changing collection + parameters with disable() method. The enable() method resumes metric + collection. + """ + + def __init__(self, mgr, vbox): + """ Initializes the instance. + + """ + self.mgr = mgr + self.isMscom = (mgr.type == 'MSCOM') + self.collector = vbox.performanceCollector + + def setup(self, names, objects, period, nsamples): + """ Discards all previously collected values for the specified + metrics, sets the period of collection and the number of retained + samples, enables collection. + """ + self.collector.setupMetrics(names, objects, period, nsamples) + + def enable(self, names, objects): + """ Resumes metric collection for the specified metrics. + """ + self.collector.enableMetrics(names, objects) + + def disable(self, names, objects): + """ Suspends metric collection for the specified metrics. + """ + self.collector.disableMetrics(names, objects) + + def query(self, names, objects): + """ Retrieves collected metric values as well as some auxiliary + information. Returns an array of dictionaries, one dictionary per + metric. Each dictionary contains the following entries: + 'name': metric name + 'object': managed object this metric associated with + 'unit': unit of measurement + 'scale': divide 'values' by this number to get float numbers + 'values': collected data + 'values_as_string': pre-processed values ready for 'print' statement + """ + # Get around the problem with input arrays returned in output + # parameters (see #3953) for MSCOM. + if self.isMscom: + (values, names, objects, names_out, objects_out, units, scales, sequence_numbers, + indices, lengths) = self.collector.queryMetricsData(names, objects) + else: + (values, names_out, objects_out, units, scales, sequence_numbers, + indices, lengths) = self.collector.queryMetricsData(names, objects) + out = [] + for i in xrange(0, len(names_out)): + scale = int(scales[i]) + if scale != 1: + fmt = '%.2f%s' + else: + fmt = '%d %s' + out.append({ + 'name': str(names_out[i]), + 'object': str(objects_out[i]), + 'unit': str(units[i]), + 'scale': scale, + 'values': [int(values[j]) for j in xrange(int(indices[i]), int(indices[i]) + int(lengths[i]))], + 'values_as_string': '[' + ', '.join([fmt % (int(values[j]) / scale, units[i]) for j in + xrange(int(indices[i]), int(indices[i]) + int(lengths[i]))]) + ']' + }) + return out + + +# +# Attribute hacks. +# +def ComifyName(name): + return name[0].capitalize() + name[1:] + + +## This is for saving the original DispatchBaseClass __getattr__ and __setattr__ +# method references. +_g_dCOMForward = { + 'getattr': None, + 'setattr': None, +} + + +def _CustomGetAttr(self, sAttr): + """ Our getattr replacement for DispatchBaseClass. """ + # Fastpath. + oRet = self.__class__.__dict__.get(sAttr) + if oRet is not None: + return oRet + + # Try case-insensitivity workaround for class attributes (COM methods). + sAttrLower = sAttr.lower() + for k in list(self.__class__.__dict__.keys()): + if k.lower() == sAttrLower: + setattr(self.__class__, sAttr, self.__class__.__dict__[k]) + return getattr(self, k) + + # Slow path. + try: + return _g_dCOMForward['getattr'](self, ComifyName(sAttr)) + except AttributeError: + return _g_dCOMForward['getattr'](self, sAttr) + + +def _CustomSetAttr(self, sAttr, oValue): + """ Our setattr replacement for DispatchBaseClass. """ + try: + return _g_dCOMForward['setattr'](self, ComifyName(sAttr), oValue) + except AttributeError: + return _g_dCOMForward['setattr'](self, sAttr, oValue) + + +class PlatformBase(object): + """ + Base class for the platform specific code. + """ + + def __init__(self, aoParams): + _ = aoParams + + def getVirtualBox(self): + """ + Gets a the IVirtualBox singleton. + """ + return None + + def getSessionObject(self): + """ + Get a session object that can be used for opening machine sessions. + + The oIVBox parameter is an getVirtualBox() return value, i.e. an + IVirtualBox reference. + + See also openMachineSession. + """ + return None + + def getType(self): + """ Returns the platform type (class name sans 'Platform'). """ + return None + + def isRemote(self): + """ + Returns True if remote (web services) and False if local (COM/XPCOM). + """ + return False + + def getArray(self, oInterface, sAttrib): + """ + Retrives the value of the array attribute 'sAttrib' from + interface 'oInterface'. + + This is for hiding platform specific differences in attributes + returning arrays. + """ + _ = oInterface + _ = sAttrib + return None + + def setArray(self, oInterface, sAttrib, aoArray): + """ + Sets the value (aoArray) of the array attribute 'sAttrib' in + interface 'oInterface'. + + This is for hiding platform specific differences in attributes + setting arrays. + """ + _ = oInterface + _ = sAttrib + _ = aoArray + return None + + def initPerThread(self): + """ + Does backend specific initialization for the calling thread. + """ + return True + + def deinitPerThread(self): + """ + Does backend specific uninitialization for the calling thread. + """ + return True + + def createListener(self, oImplClass, dArgs): + """ + Instantiates and wraps an active event listener class so it can be + passed to an event source for registration. + + oImplClass is a class (type, not instance) which implements + IEventListener. + + dArgs is a dictionary with string indexed variables. This may be + modified by the method to pass platform specific parameters. Can + be None. + + This currently only works on XPCOM. COM support is not possible due to + shortcuts taken in the COM bridge code, which is not under our control. + Use passive listeners for COM and web services. + """ + _ = oImplClass + _ = dArgs + raise Exception("No active listeners for this platform") + + def waitForEvents(self, cMsTimeout): + """ + Wait for events to arrive and process them. + + The timeout (cMsTimeout) is in milliseconds for how long to wait for + events to arrive. A negative value means waiting for ever, while 0 + does not wait at all. + + Returns 0 if events was processed. + Returns 1 if timed out or interrupted in some way. + Returns 2 on error (like not supported for web services). + + Raises an exception if the calling thread is not the main thread (the one + that initialized VirtualBoxManager) or if the time isn't an integer. + """ + _ = cMsTimeout + return 2 + + def interruptWaitEvents(self): + """ + Interrupt a waitForEvents call. + This is normally called from a worker thread to wake up the main thread. + + Returns True on success, False on failure. + """ + return False + + def deinit(self): + """ + Unitializes the platform specific backend. + """ + return None + + def queryInterface(self, oIUnknown, sClassName): + """ + IUnknown::QueryInterface wrapper. + + oIUnknown is who to ask. + sClassName is the name of the interface we're asking for. + """ + return None + + # + # Error (exception) access methods. + # + + def xcptGetStatus(self, oXcpt): + """ + Returns the COM status code from the VBox API given exception. + """ + return None + + def xcptIsDeadInterface(self, oXcpt): + """ + Returns True if the exception indicates that the interface is dead, False if not. + """ + return False + + def xcptIsEqual(self, oXcpt, hrStatus): + """ + Checks if the exception oXcpt is equal to the COM/XPCOM status code + hrStatus. + + The oXcpt parameter can be any kind of object, we'll just return True + if it doesn't behave like a our exception class. + + Will not raise any exception as long as hrStatus and self are not bad. + """ + try: + hrXcpt = self.xcptGetStatus(oXcpt) + except AttributeError: + return False + if hrXcpt == hrStatus: + return True + + # Fudge for 32-bit signed int conversion. + if 0x7fffffff < hrStatus <= 0xffffffff and hrXcpt < 0: + if (hrStatus - 0x100000000) == hrXcpt: + return True + return False + + def xcptGetMessage(self, oXcpt): + """ + Returns the best error message found in the COM-like exception. + Returns None to fall back on xcptToString. + Raises exception if oXcpt isn't our kind of exception object. + """ + return None + + def xcptGetBaseXcpt(self): + """ + Returns the base exception class. + """ + return None + + def xcptSetupConstants(self, oDst): + """ + Copy/whatever all error constants onto oDst. + """ + return oDst + + @staticmethod + def xcptCopyErrorConstants(oDst, oSrc): + """ + Copy everything that looks like error constants from oDst to oSrc. + """ + for sAttr in dir(oSrc): + if sAttr[0].isupper() and (sAttr[1].isupper() or sAttr[1] == '_'): + oAttr = getattr(oSrc, sAttr) + if type(oAttr) is int: + setattr(oDst, sAttr, oAttr) + return oDst + + +class PlatformMSCOM(PlatformBase): + """ + Platform specific code for MS COM. + """ + + ## @name VirtualBox COM Typelib definitions (should be generate) + # + # @remarks Must be updated when the corresponding VirtualBox.xidl bits + # are changed. Fortunately this isn't very often. + # @{ + VBOX_TLB_GUID = '{D7569351-1750-46F0-936E-BD127D5BC264}' + VBOX_TLB_LCID = 0 + VBOX_TLB_MAJOR = 1 + VBOX_TLB_MINOR = 3 + ## @} + + def __init__(self, dParams): + PlatformBase.__init__(self, dParams) + + # + # Since the code runs on all platforms, we have to do a lot of + # importing here instead of at the top of the file where it's normally located. + # + from win32com import universal + from win32com.client import gencache, DispatchBaseClass + from win32com.client import constants, getevents + import win32com + import pythoncom + import win32api + import winerror + from win32con import DUPLICATE_SAME_ACCESS + from win32api import GetCurrentThread, GetCurrentThreadId, DuplicateHandle, GetCurrentProcess + import threading + + self.winerror = winerror + + # Setup client impersonation in COM calls. + try: + pythoncom.CoInitializeSecurity(None, + None, + None, + pythoncom.RPC_C_AUTHN_LEVEL_DEFAULT, + pythoncom.RPC_C_IMP_LEVEL_IMPERSONATE, + None, + pythoncom.EOAC_NONE, + None) + except: + _, oXcpt, _ = sys.exc_info(); + if isinstance(oXcpt, pythoncom.com_error) and self.xcptGetStatus(oXcpt) == -2147417831: # RPC_E_TOO_LATE + print("Warning: CoInitializeSecurity was already called"); + else: + print("Warning: CoInitializeSecurity failed: ", oXcpt); + + # Remember this thread ID and get its handle so we can wait on it in waitForEvents(). + self.tid = GetCurrentThreadId() + pid = GetCurrentProcess() + self.aoHandles = [DuplicateHandle(pid, GetCurrentThread(), pid, 0, 0, DUPLICATE_SAME_ACCESS),] # type: list[PyHANDLE] + + # Hack the COM dispatcher base class so we can modify method and + # attribute names to match those in xpcom. + if _g_dCOMForward['setattr'] is None: + _g_dCOMForward['getattr'] = DispatchBaseClass.__dict__['__getattr__'] + _g_dCOMForward['setattr'] = DispatchBaseClass.__dict__['__setattr__'] + setattr(DispatchBaseClass, '__getattr__', _CustomGetAttr) + setattr(DispatchBaseClass, '__setattr__', _CustomSetAttr) + + # Hack the exception base class so the users doesn't need to check for + # XPCOM or COM and do different things. + ## @todo + + # + # Make sure the gencache is correct (we don't quite follow the COM + # versioning rules). + # + self.flushGenPyCache(win32com.client.gencache) + win32com.client.gencache.EnsureDispatch('VirtualBox.Session') + win32com.client.gencache.EnsureDispatch('VirtualBox.VirtualBox') + win32com.client.gencache.EnsureDispatch('VirtualBox.VirtualBoxClient') + + self.oClient = None ##< instance of client used to support lifetime of VBoxSDS + self.oIntCv = threading.Condition() + self.fInterrupted = False + + _ = dParams + + def flushGenPyCache(self, oGenCache): + """ + Flushes VBox related files in the win32com gen_py cache. + + This is necessary since we don't follow the typelib versioning rules + that everyeone else seems to subscribe to. + """ + # + # The EnsureModule method have broken validation code, it doesn't take + # typelib module directories into account. So we brute force them here. + # (It's possible the directory approach is from some older pywin + # version or the result of runnig makepy or gencache manually, but we + # need to cover it as well.) + # + sName = oGenCache.GetGeneratedFileName(self.VBOX_TLB_GUID, self.VBOX_TLB_LCID, + self.VBOX_TLB_MAJOR, self.VBOX_TLB_MINOR) + sGenPath = oGenCache.GetGeneratePath() + if len(sName) > 36 and len(sGenPath) > 5: + sTypelibPath = os.path.join(sGenPath, sName) + if os.path.isdir(sTypelibPath): + import shutil + shutil.rmtree(sTypelibPath, ignore_errors=True) + + # + # Ensure that our typelib is valid. + # + return oGenCache.EnsureModule(self.VBOX_TLB_GUID, self.VBOX_TLB_LCID, self.VBOX_TLB_MAJOR, self.VBOX_TLB_MINOR) + + def getSessionObject(self): + import win32com + from win32com.client import Dispatch + return win32com.client.Dispatch("VirtualBox.Session") + + def getVirtualBox(self): + # Caching self.oClient is the trick for SDS. It allows to keep the + # VBoxSDS in the memory until the end of PlatformMSCOM lifetme. + if self.oClient is None: + import win32com + from win32com.client import Dispatch + self.oClient = win32com.client.Dispatch("VirtualBox.VirtualBoxClient") + return self.oClient.virtualBox + + def getType(self): + return 'MSCOM' + + def getArray(self, oInterface, sAttrib): + return oInterface.__getattr__(sAttrib) + + def setArray(self, oInterface, sAttrib, aoArray): + # + # HACK ALERT! + # + # With pywin32 build 218, we're seeing type mismatch errors here for + # IGuestSession::environmentChanges (safearray of BSTRs). The Dispatch + # object (_oleobj_) seems to get some type conversion wrong and COM + # gets upset. So, we redo some of the dispatcher work here, picking + # the missing type information from the getter. + # + oOleObj = getattr(oInterface, '_oleobj_') + aPropMapGet = getattr(oInterface, '_prop_map_get_') + aPropMapPut = getattr(oInterface, '_prop_map_put_') + sComAttrib = sAttrib if sAttrib in aPropMapGet else ComifyName(sAttrib) + try: + aArgs, aDefaultArgs = aPropMapPut[sComAttrib] + aGetArgs = aPropMapGet[sComAttrib] + except KeyError: # fallback. + return oInterface.__setattr__(sAttrib, aoArray) + + import pythoncom + oOleObj.InvokeTypes(aArgs[0], # dispid + aArgs[1], # LCID + aArgs[2], # DISPATCH_PROPERTYPUT + (pythoncom.VT_HRESULT, 0), # retType - or void? + (aGetArgs[2],), # argTypes - trick: we get the type from the getter. + aoArray,) # The array + + def initPerThread(self): + import pythoncom + pythoncom.CoInitializeEx(0) + + def deinitPerThread(self): + import pythoncom + pythoncom.CoUninitialize() + + def createListener(self, oImplClass, dArgs): + if True: + raise Exception('no active listeners on Windows as PyGatewayBase::QueryInterface() ' + 'returns new gateway objects all the time, thus breaking EventQueue ' + 'assumptions about the listener interface pointer being constants between calls ') + # Did this code ever really work? + d = {} + d['BaseClass'] = oImplClass + d['dArgs'] = dArgs + d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID + d['tlb_major'] = PlatformMSCOM.VBOX_TLB_MAJOR + d['tlb_minor'] = PlatformMSCOM.VBOX_TLB_MINOR + str_ = "" + str_ += "import win32com.server.util\n" + str_ += "import pythoncom\n" + + str_ += "class ListenerImpl(BaseClass):\n" + str_ += " _com_interfaces_ = ['IEventListener']\n" + str_ += " _typelib_guid_ = tlb_guid\n" + str_ += " _typelib_version_ = tlb_major, tlb_minor\n" + str_ += " _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n" + # Maybe we'd better implement Dynamic invoke policy, to be more flexible here + str_ += " _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n" + + # capitalized version of listener method + str_ += " HandleEvent=BaseClass.handleEvent\n" + str_ += " def __init__(self): BaseClass.__init__(self, dArgs)\n" + str_ += "result = win32com.server.util.wrap(ListenerImpl())\n" + exec(str_, d, d) + return d['result'] + + def waitForEvents(self, timeout): + from win32api import GetCurrentThreadId + from win32event import INFINITE + from win32event import MsgWaitForMultipleObjects, QS_ALLINPUT, WAIT_TIMEOUT, WAIT_OBJECT_0 + from pythoncom import PumpWaitingMessages + import types + + if not isinstance(timeout, int): + raise TypeError("The timeout argument is not an integer") + if self.tid != GetCurrentThreadId(): + raise Exception("wait for events from the same thread you inited!") + + if timeout < 0: + cMsTimeout = INFINITE + else: + cMsTimeout = timeout + rc = MsgWaitForMultipleObjects(self.aoHandles, 0, cMsTimeout, QS_ALLINPUT) + if WAIT_OBJECT_0 <= rc < WAIT_OBJECT_0 + len(self.aoHandles): + # is it possible? + rc = 2 + elif rc == WAIT_OBJECT_0 + len(self.aoHandles): + # Waiting messages + PumpWaitingMessages() + rc = 0 + else: + # Timeout + rc = 1 + + # check for interruption + self.oIntCv.acquire() + if self.fInterrupted: + self.fInterrupted = False + rc = 1 + self.oIntCv.release() + + return rc + + def interruptWaitEvents(self): + """ + Basically a python implementation of NativeEventQueue::postEvent(). + + The magic value must be in sync with the C++ implementation or this + won't work. + + Note that because of this method we cannot easily make use of a + non-visible Window to handle the message like we would like to do. + """ + from win32api import PostThreadMessage + from win32con import WM_USER + + self.oIntCv.acquire() + self.fInterrupted = True + self.oIntCv.release() + try: + PostThreadMessage(self.tid, WM_USER, None, 0xf241b819) + except: + return False + return True + + def deinit(self): + for oHandle in self.aoHandles: + if oHandle is not None: + oHandle.Close(); + self.oHandle = None; + + del self.oClient; + self.oClient = None; + + # This non-sense doesn't pair up with any pythoncom.CoInitialize[Ex]. + # See @bugref{9037}. + #import pythoncom + #pythoncom.CoUninitialize() + + def queryInterface(self, oIUnknown, sClassName): + from win32com.client import CastTo + return CastTo(oIUnknown, sClassName) + + def xcptGetStatus(self, oXcpt): + # The DISP_E_EXCEPTION + excptinfo fun needs checking up, only + # empirical info on it so far. + hrXcpt = oXcpt.hresult + if hrXcpt == self.winerror.DISP_E_EXCEPTION: + try: + hrXcpt = oXcpt.excepinfo[5] + except: + pass + return hrXcpt + + def xcptIsDeadInterface(self, oXcpt): + return self.xcptGetStatus(oXcpt) in [ + 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE. + 0x800706be, -2147023170, # RPC_S_CALL_FAILED. + 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE. + 0x80010108, -2147417848, # RPC_E_DISCONNECTED. + 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF + ] + + def xcptGetMessage(self, oXcpt): + if hasattr(oXcpt, 'excepinfo'): + try: + if len(oXcpt.excepinfo) >= 3: + sRet = oXcpt.excepinfo[2] + if len(sRet) > 0: + return sRet[0:] + except: + pass + if hasattr(oXcpt, 'strerror'): + try: + sRet = oXcpt.strerror + if len(sRet) > 0: + return sRet + except: + pass + return None + + def xcptGetBaseXcpt(self): + import pythoncom + + return pythoncom.com_error + + def xcptSetupConstants(self, oDst): + import winerror + + oDst = self.xcptCopyErrorConstants(oDst, winerror) + + # XPCOM compatability constants. + oDst.NS_OK = oDst.S_OK + oDst.NS_ERROR_FAILURE = oDst.E_FAIL + oDst.NS_ERROR_ABORT = oDst.E_ABORT + oDst.NS_ERROR_NULL_POINTER = oDst.E_POINTER + oDst.NS_ERROR_NO_INTERFACE = oDst.E_NOINTERFACE + oDst.NS_ERROR_INVALID_ARG = oDst.E_INVALIDARG + oDst.NS_ERROR_OUT_OF_MEMORY = oDst.E_OUTOFMEMORY + oDst.NS_ERROR_NOT_IMPLEMENTED = oDst.E_NOTIMPL + oDst.NS_ERROR_UNEXPECTED = oDst.E_UNEXPECTED + return oDst + + +class PlatformXPCOM(PlatformBase): + """ + Platform specific code for XPCOM. + """ + + def __init__(self, dParams): + PlatformBase.__init__(self, dParams) + sys.path.append(VBoxSdkDir + '/bindings/xpcom/python/') + import xpcom.vboxxpcom + import xpcom + import xpcom.components + _ = dParams + + def getSessionObject(self): + import xpcom.components + return xpcom.components.classes["@virtualbox.org/Session;1"].createInstance() + + def getVirtualBox(self): + import xpcom.components + client = xpcom.components.classes["@virtualbox.org/VirtualBoxClient;1"].createInstance() + return client.virtualBox + + def getType(self): + return 'XPCOM' + + def getArray(self, oInterface, sAttrib): + return oInterface.__getattr__('get' + ComifyName(sAttrib))() + + def setArray(self, oInterface, sAttrib, aoArray): + return oInterface.__getattr__('set' + ComifyName(sAttrib))(aoArray) + + def initPerThread(self): + import xpcom + xpcom._xpcom.AttachThread() + + def deinitPerThread(self): + import xpcom + xpcom._xpcom.DetachThread() + + def createListener(self, oImplClass, dArgs): + d = {} + d['BaseClass'] = oImplClass + d['dArgs'] = dArgs + str = "" + str += "import xpcom.components\n" + str += "class ListenerImpl(BaseClass):\n" + str += " _com_interfaces_ = xpcom.components.interfaces.IEventListener\n" + str += " def __init__(self): BaseClass.__init__(self, dArgs)\n" + str += "result = ListenerImpl()\n" + exec(str, d, d) + return d['result'] + + def waitForEvents(self, timeout): + import xpcom + return xpcom._xpcom.WaitForEvents(timeout) + + def interruptWaitEvents(self): + import xpcom + return xpcom._xpcom.InterruptWait() + + def deinit(self): + import xpcom + xpcom._xpcom.DeinitCOM() + + def queryInterface(self, oIUnknown, sClassName): + import xpcom.components + return oIUnknown.queryInterface(getattr(xpcom.components.interfaces, sClassName)) + + def xcptGetStatus(self, oXcpt): + return oXcpt.errno + + def xcptIsDeadInterface(self, oXcpt): + return self.xcptGetStatus(oXcpt) in [ + 0x80004004, -2147467260, # NS_ERROR_ABORT + 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED) + ] + + def xcptGetMessage(self, oXcpt): + if hasattr(oXcpt, 'msg'): + try: + sRet = oXcpt.msg + if len(sRet) > 0: + return sRet + except: + pass + return None + + def xcptGetBaseXcpt(self): + import xpcom + return xpcom.Exception + + def xcptSetupConstants(self, oDst): + import xpcom + oDst = self.xcptCopyErrorConstants(oDst, xpcom.nsError) + + # COM compatability constants. + oDst.E_ACCESSDENIED = -2147024891 # see VBox/com/defs.h + oDst.S_OK = oDst.NS_OK + oDst.E_FAIL = oDst.NS_ERROR_FAILURE + oDst.E_ABORT = oDst.NS_ERROR_ABORT + oDst.E_POINTER = oDst.NS_ERROR_NULL_POINTER + oDst.E_NOINTERFACE = oDst.NS_ERROR_NO_INTERFACE + oDst.E_INVALIDARG = oDst.NS_ERROR_INVALID_ARG + oDst.E_OUTOFMEMORY = oDst.NS_ERROR_OUT_OF_MEMORY + oDst.E_NOTIMPL = oDst.NS_ERROR_NOT_IMPLEMENTED + oDst.E_UNEXPECTED = oDst.NS_ERROR_UNEXPECTED + oDst.DISP_E_EXCEPTION = -2147352567 # For COM compatability only. + return oDst + + +class PlatformWEBSERVICE(PlatformBase): + """ + VirtualBox Web Services API specific code. + """ + + def __init__(self, dParams): + PlatformBase.__init__(self, dParams) + # Import web services stuff. Fix the sys.path the first time. + sWebServLib = os.path.join(VBoxSdkDir, 'bindings', 'webservice', 'python', 'lib') + if sWebServLib not in sys.path: + sys.path.append(sWebServLib) + import VirtualBox_wrappers + from VirtualBox_wrappers import IWebsessionManager2 + + # Initialize instance variables from parameters. + if dParams is not None: + self.user = dParams.get("user", "") + self.password = dParams.get("password", "") + self.url = dParams.get("url", "") + else: + self.user = "" + self.password = "" + self.url = None + self.vbox = None + self.wsmgr = None + + # + # Base class overrides. + # + + def getSessionObject(self): + return self.wsmgr.getSessionObject(self.vbox) + + def getVirtualBox(self): + return self.connect(self.url, self.user, self.password) + + def getType(self): + return 'WEBSERVICE' + + def isRemote(self): + """ Returns True if remote VBox host, False if local. """ + return True + + def getArray(self, oInterface, sAttrib): + return oInterface.__getattr__(sAttrib) + + def setArray(self, oInterface, sAttrib, aoArray): + return oInterface.__setattr__(sAttrib, aoArray) + + def waitForEvents(self, timeout): + # Webservices cannot do that yet + return 2 + + def interruptWaitEvents(self, timeout): + # Webservices cannot do that yet + return False + + def deinit(self): + try: + self.disconnect() + except: + pass + + def queryInterface(self, oIUnknown, sClassName): + d = {} + d['oIUnknown'] = oIUnknown + str = "" + str += "from VirtualBox_wrappers import " + sClassName + "\n" + str += "result = " + sClassName + "(oIUnknown.mgr, oIUnknown.handle)\n" + # wrong, need to test if class indeed implements this interface + exec(str, d, d) + return d['result'] + + # + # Web service specific methods. + # + + def connect(self, url, user, passwd): + if self.vbox is not None: + self.disconnect() + from VirtualBox_wrappers import IWebsessionManager2 + + if url is None: + url = "" + self.url = url + if user is None: + user = "" + self.user = user + if passwd is None: + passwd = "" + self.password = passwd + self.wsmgr = IWebsessionManager2(self.url) + self.vbox = self.wsmgr.logon(self.user, self.password) + if not self.vbox.handle: + raise Exception("cannot connect to '" + self.url + "' as '" + self.user + "'") + return self.vbox + + def disconnect(self): + if self.vbox is not None and self.wsmgr is not None: + self.wsmgr.logoff(self.vbox) + self.vbox = None + self.wsmgr = None + + +## The current (last) exception class. +# This is reinitalized whenever VirtualBoxManager is called, so it will hold +# the reference to the error exception class for the last platform/style that +# was used. Most clients does talk to multiple VBox instance on different +# platforms at the same time, so this should be sufficent for most uses and +# be way simpler to use than VirtualBoxManager::oXcptClass. +CurXcptClass = None + + +class VirtualBoxManager(object): + """ + VirtualBox API manager class. + + The API users will have to instantiate this. If no parameters are given, + it will default to interface with the VirtualBox running on the local + machine. sStyle can be None (default), MSCOM, XPCOM or WEBSERVICES. Most + users will either be specifying None or WEBSERVICES. + + The dPlatformParams is an optional dictionary for passing parameters to the + WEBSERVICE backend. + """ + + class Statuses(object): + def __init__(self): + pass + + def __init__(self, sStyle=None, dPlatformParams=None): + if sStyle is None: + if sys.platform == 'win32': + sStyle = "MSCOM" + else: + sStyle = "XPCOM" + if sStyle == 'XPCOM': + self.platform = PlatformXPCOM(dPlatformParams) + elif sStyle == 'MSCOM': + self.platform = PlatformMSCOM(dPlatformParams) + elif sStyle == 'WEBSERVICE': + self.platform = PlatformWEBSERVICE(dPlatformParams) + else: + raise Exception('Unknown sStyle=%s' % (sStyle,)) + self.style = sStyle + self.type = self.platform.getType() + self.remote = self.platform.isRemote() + ## VirtualBox API constants (for webservices, enums are symbolic). + self.constants = VirtualBoxReflectionInfo(sStyle == "WEBSERVICE") + + ## Status constants. + self.statuses = self.platform.xcptSetupConstants(VirtualBoxManager.Statuses()) + ## @todo Add VBOX_E_XXX to statuses? They're already in constants... + ## Dictionary for errToString, built on demand. + self._dErrorValToName = None + + ## Dictionary for resolving enum values to names, two levels of dictionaries. + ## First level is indexed by enum name, the next by value. + self._ddEnumValueToName = {}; + + ## The exception class for the selected platform. + self.oXcptClass = self.platform.xcptGetBaseXcpt() + global CurXcptClass + CurXcptClass = self.oXcptClass + + # Get the virtualbox singleton. + try: + vbox = self.platform.getVirtualBox() + except NameError: + print("Installation problem: check that appropriate libs in place") + traceback.print_exc() + raise + except Exception: + _, e, _ = sys.exc_info() + print("init exception: ", e) + traceback.print_exc() + + def __del__(self): + self.deinit() + + def getPythonApiRevision(self): + """ + Returns a Python API revision number. + This will be incremented when features are added to this file. + """ + return 3 + + @property + def mgr(self): + """ + This used to be an attribute referring to a session manager class with + only one method called getSessionObject. It moved into this class. + """ + return self + + # + # Wrappers for self.platform methods. + # + def getVirtualBox(self): + """ See PlatformBase::getVirtualBox(). """ + return self.platform.getVirtualBox() + + def getSessionObject(self, oIVBox = None): + """ See PlatformBase::getSessionObject(). """ + # ignore parameter which was never needed + _ = oIVBox + return self.platform.getSessionObject() + + def getArray(self, oInterface, sAttrib): + """ See PlatformBase::getArray(). """ + return self.platform.getArray(oInterface, sAttrib) + + def setArray(self, oInterface, sAttrib, aoArray): + """ See PlatformBase::setArray(). """ + return self.platform.setArray(oInterface, sAttrib, aoArray) + + def createListener(self, oImplClass, dArgs=None): + """ See PlatformBase::createListener(). """ + return self.platform.createListener(oImplClass, dArgs) + + def waitForEvents(self, cMsTimeout): + """ See PlatformBase::waitForEvents(). """ + return self.platform.waitForEvents(cMsTimeout) + + def interruptWaitEvents(self): + """ See PlatformBase::interruptWaitEvents(). """ + return self.platform.interruptWaitEvents() + + def queryInterface(self, oIUnknown, sClassName): + """ See PlatformBase::queryInterface(). """ + return self.platform.queryInterface(oIUnknown, sClassName) + + # + # Init and uninit. + # + def initPerThread(self): + """ See PlatformBase::deinitPerThread(). """ + self.platform.initPerThread() + + def deinitPerThread(self): + """ See PlatformBase::deinitPerThread(). """ + return self.platform.deinitPerThread() + + def deinit(self): + """ + For unitializing the manager. + Do not access it after calling this method. + """ + if hasattr(self, "platform") and self.platform is not None: + self.platform.deinit() + self.platform = None + return True + + # + # Utility methods. + # + def openMachineSession(self, oIMachine, fPermitSharing=True): + """ + Attempts to open the a session to the machine. + Returns a session object on success. + Raises exception on failure. + """ + oSession = self.getSessionObject() + if fPermitSharing: + eType = self.constants.LockType_Shared + else: + eType = self.constants.LockType_Write + oIMachine.lockMachine(oSession, eType) + return oSession + + def closeMachineSession(self, oSession): + """ + Closes a session opened by openMachineSession. + Ignores None parameters. + """ + if oSession is not None: + oSession.unlockMachine() + return True + + def getPerfCollector(self, oIVBox): + """ + Returns a helper class (PerfCollector) for accessing performance + collector goodies. See PerfCollector for details. + """ + return PerfCollector(self, oIVBox) + + def getBinDir(self): + """ + Returns the VirtualBox binary directory. + """ + global VBoxBinDir + return VBoxBinDir + + def getSdkDir(self): + """ + Returns the VirtualBox SDK directory. + """ + global VBoxSdkDir + return VBoxSdkDir + + def getEnumValueName(self, sEnumTypeNm, oEnumValue, fTypePrefix = False): + """ + Returns the name (string) for the corresponding enum value. + """ + # Cache lookup: + dValueNames = self._ddEnumValueToName.get(sEnumTypeNm); + if dValueNames is not None: + sValueName = dValueNames.get(oEnumValue); + if sValueName: + return sValueName if not fTypePrefix else '%s_%s' % (sEnumTypeNm, sValueName); + else: + # Cache miss. Build the reverse lookup dictionary and add it to the cache: + dNamedValues = self.constants.all_values(sEnumTypeNm); + if len(dNamedValues) > 0: + + dValueNames = dict(); + for sName in dNamedValues: + dValueNames[dNamedValues[sName]] = sName; + self._ddEnumValueToName[sEnumTypeNm] = dValueNames; + + # Lookup the value: + sValueName = dValueNames.get(oEnumValue); + if sValueName: + return sValueName if not fTypePrefix else '%s_%s' % (sEnumTypeNm, sValueName); + + # Fallback: + return '%s_Unknown_%s' % (sEnumTypeNm, oEnumValue); + + # + # Error code utilities. + # + ## @todo port to webservices! + def xcptGetStatus(self, oXcpt=None): + """ + Gets the status code from an exception. If the exception parameter + isn't specified, the current exception is examined. + """ + if oXcpt is None: + oXcpt = sys.exc_info()[1] + return self.platform.xcptGetStatus(oXcpt) + + def xcptIsDeadInterface(self, oXcpt=None): + """ + Returns True if the exception indicates that the interface is dead, + False if not. If the exception parameter isn't specified, the current + exception is examined. + """ + if oXcpt is None: + oXcpt = sys.exc_info()[1] + return self.platform.xcptIsDeadInterface(oXcpt) + + def xcptIsOurXcptKind(self, oXcpt=None): + """ + Checks if the exception is one that could come from the VBox API. If + the exception parameter isn't specified, the current exception is + examined. + """ + if self.oXcptClass is None: # @todo find the exception class for web services! + return False + if oXcpt is None: + oXcpt = sys.exc_info()[1] + return isinstance(oXcpt, self.oXcptClass) + + def xcptIsEqual(self, oXcpt, hrStatus): + """ + Checks if the exception oXcpt is equal to the COM/XPCOM status code + hrStatus. + + The oXcpt parameter can be any kind of object, we'll just return True + if it doesn't behave like a our exception class. If it's None, we'll + query the current exception and examine that. + + Will not raise any exception as long as hrStatus and self are not bad. + """ + if oXcpt is None: + oXcpt = sys.exc_info()[1] + return self.platform.xcptIsEqual(oXcpt, hrStatus) + + def xcptIsNotEqual(self, oXcpt, hrStatus): + """ + Negated xcptIsEqual. + """ + return not self.xcptIsEqual(oXcpt, hrStatus) + + def xcptToString(self, hrStatusOrXcpt=None): + """ + Converts the specified COM status code, or the status code of the + specified exception, to a C constant string. If the parameter isn't + specified (is None), the current exception is examined. + """ + + # Deal with exceptions. + if hrStatusOrXcpt is None or self.xcptIsOurXcptKind(hrStatusOrXcpt): + hrStatus = self.xcptGetStatus(hrStatusOrXcpt) + else: + hrStatus = hrStatusOrXcpt + + # Build the dictionary on demand. + if self._dErrorValToName is None: + dErrorValToName = dict() + for sKey in dir(self.statuses): + if sKey[0].isupper(): + oValue = getattr(self.statuses, sKey) + if isinstance(oValue, (int, long)): + dErrorValToName[int(oValue)] = sKey + # Always prefer the COM names (see aliasing in platform specific code): + for sKey in ('S_OK', 'E_FAIL', 'E_ABORT', 'E_POINTER', 'E_NOINTERFACE', 'E_INVALIDARG', + 'E_OUTOFMEMORY', 'E_NOTIMPL', 'E_UNEXPECTED',): + oValue = getattr(self.statuses, sKey, None) + if oValue is not None: + dErrorValToName[oValue] = sKey + self._dErrorValToName = dErrorValToName + + # Do the lookup, falling back on formatting the status number. + try: + sStr = self._dErrorValToName[int(hrStatus)] + except KeyError: + hrLong = long(hrStatus) + sStr = '%#x (%d)' % (hrLong & 0xffffffff, hrLong) + return sStr + + def xcptGetMessage(self, oXcpt=None): + """ + Returns the best error message found in the COM-like exception. If the + exception parameter isn't specified, the current exception is examined. + """ + if oXcpt is None: + oXcpt = sys.exc_info()[1] + sRet = self.platform.xcptGetMessage(oXcpt) + if sRet is None: + sRet = self.xcptToString(oXcpt) + return sRet + diff --git a/src/VBox/Main/glue/xpcom/Makefile.kup b/src/VBox/Main/glue/xpcom/Makefile.kup new file mode 100644 index 00000000..e69de29b diff --git a/src/VBox/Main/glue/xpcom/helpers.cpp b/src/VBox/Main/glue/xpcom/helpers.cpp new file mode 100644 index 00000000..bdfa3591 --- /dev/null +++ b/src/VBox/Main/glue/xpcom/helpers.cpp @@ -0,0 +1,204 @@ +/* $Id: helpers.cpp $ */ +/** @file + * COM helper functions for XPCOM + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "VBox/com/defs.h" +#include +#include +#include + + +// +// OLE Automation string APIs +// + +// Note: on Windows, every BSTR stores its length in the +// byte just before the pointer you get. If we do it like this, +// the caller cannot just use nsMemory::Free() on our strings. +// Therefore we'll try to implement it differently and hope that +// we don't run into problems. + +/** + * Copies a string into a new memory block including the terminating UCS2 NULL. + * + * @param pwsz Source string to duplicate. + * @returns New BSTR string buffer. + */ +BSTR SysAllocString(const OLECHAR *pwszSrc) +{ + AssertCompile(sizeof(*pwszSrc) == sizeof(PRUnichar)); + if (pwszSrc) + return SysAllocStringLen(pwszSrc, RTUtf16Len((PCRTUTF16)pwszSrc)); + return NULL; +} + +/** + * Duplicates an ANSI string into a BSTR / allocates a BSTR with a size given in + * bytes. + * + * No conversion is done. + * + * @param pszSrc Source string to copy, optional. + * @param cbSrcReq Length of the source string / memory request in bytes. + * @returns new BSTR string buffer, NULL on failure. + */ +BSTR SysAllocStringByteLen(char const *pszSrc, unsigned int cbSrcReq) +{ + BSTR pBstrNew = (BSTR)nsMemory::Alloc(RT_ALIGN_Z(cbSrcReq + sizeof(OLECHAR), sizeof(OLECHAR))); + AssertCompile(sizeof(*pBstrNew) == sizeof(OLECHAR)); + if (pBstrNew) + { + if (!pszSrc) + memset(pBstrNew, 0, cbSrcReq + sizeof(OLECHAR)); + else + { + // Copy the string and make sure it is terminated. + memcpy(pBstrNew, pszSrc, cbSrcReq); + char *pchTerminator = (char *)pBstrNew; + pchTerminator[cbSrcReq] = '\0'; + pchTerminator[cbSrcReq + 1] = '\0'; + } + } + return pBstrNew; +} + +/** + * Duplicates a UTF-16 string into a BSTR / Allocates a BSTR with a size given + * in UTF-16 characters. + * + * @param pwszSrc Pointer to the source string, optional. + * @param cwcSrcReq Length of the source string / memory request in UTF-16 + * characters. + * @returns new BSTR string buffer, NULL on failure. + */ +BSTR SysAllocStringLen(const OLECHAR *pwszSrc, unsigned int cwcSrcReq) +{ + size_t const cbReq = (cwcSrcReq + 1) * sizeof(OLECHAR); + BSTR pBstrNew = (BSTR)nsMemory::Alloc(cbReq); + AssertCompile(sizeof(*pBstrNew) == sizeof(OLECHAR)); + if (pBstrNew) + { + if (!pwszSrc) + memset(pBstrNew, 0, cbReq); + else + { + // Copy the string and make sure it is terminated. + memcpy(pBstrNew, pwszSrc, cbReq - sizeof(OLECHAR)); + pBstrNew[cwcSrcReq] = L'\0'; + } + } + return pBstrNew; +} + +/** + * Frees the memory associated with the given BSTR. + * + * @param pBstr The string to free. NULL is ignored. + */ +void SysFreeString(BSTR pBstr) +{ + if (pBstr) + nsMemory::Free(pBstr); +} + +/** + * Duplicates @a pwszSrc into an exsting BSTR, adjust its size to make it fit. + * + * @param ppBstr The existing BSTR buffer pointer. + * @param pwszSrc Source string to copy. If NULL, the existing BSTR is freed. + * @returns success indicator (TRUE/FALSE) + */ +int SysReAllocString(BSTR *ppBstr, const OLECHAR *pwszSrc) +{ + if (pwszSrc) + return SysReAllocStringLen(ppBstr, pwszSrc, RTUtf16Len((PCRTUTF16)pwszSrc)); + SysFreeString(*ppBstr); + *ppBstr = NULL; + return 1; +} + +/** + * Duplicates @a pwszSrc into an exsting BSTR / resizing an existing BSTR buffer + * into the given size (@a cwcSrcReq). + * + * @param ppBstr The existing BSTR buffer pointer. + * @param pwszSrc Source string to copy into the adjusted pbstr, optional. + * @param cwcSrcReq Length of the source string / request in UCS2 + * characters, a zero terminator is always added. + * @returns success indicator (TRUE/FALSE) + */ +int SysReAllocStringLen(BSTR *ppBstr, const OLECHAR *pwszSrc, unsigned int cwcSrcReq) +{ + BSTR pBstrOld = *ppBstr; + AssertCompile(sizeof(*pBstrOld) == sizeof(OLECHAR)); + if (pBstrOld) + { + if ((BSTR)pwszSrc == pBstrOld) + pwszSrc = NULL; + + size_t cbReq = (cwcSrcReq + 1) * sizeof(OLECHAR); + BSTR pBstrNew = (BSTR)nsMemory::Realloc(pBstrOld, cbReq); + if (pBstrNew) + { + if (pwszSrc) + memcpy(pBstrNew, pwszSrc, cbReq - sizeof(OLECHAR)); + pBstrNew[cwcSrcReq] = L'\0'; + *ppBstr = pBstrNew; + return 1; + } + } + else + { + // allocate a new string + *ppBstr = SysAllocStringLen(pwszSrc, cwcSrcReq); + if (*ppBstr) + return 1; + } + return 0; +} + +/** + * Returns the string length in bytes without the terminator. + * + * @param pBstr The BSTR to get the byte length of. + * @returns String length in bytes. + */ +unsigned int SysStringByteLen(BSTR pBstr) +{ + AssertCompile(sizeof(OLECHAR) == sizeof(*pBstr)); + return RTUtf16Len(pBstr) * sizeof(OLECHAR); +} + +/** + * Returns the string length in OLECHARs without the terminator. + * + * @param pBstr The BSTR to get the length of. + * @returns String length in OLECHARs. + */ +unsigned int SysStringLen(BSTR pBstr) +{ + return RTUtf16Len(pBstr); +} -- cgit v1.2.3