ExtPackManagerImpl.cpp revision 20d5cd0381367988b3d67ba5ff2b27dc109dfe2d
/* $Id$ */
/** @file
* VirtualBox Main - interface for Extension Packs, VBoxSVC & VBoxC.
*/
/*
* Copyright (C) 2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "ExtPackManagerImpl.h"
#include "ExtPackUtil.h"
#include <iprt/buildconfig.h>
#include "AutoCaller.h"
#include "Global.h"
#include "SystemPropertiesImpl.h"
#include "VirtualBoxImpl.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** @name VBOX_EXTPACK_HELPER_NAME
* The name of the utility application we employ to install and uninstall the
* extension packs. This is a set-uid-to-root binary on unixy platforms, which
* is why it has to be a separate application.
*/
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
# define VBOX_EXTPACK_HELPER_NAME "VBoxExtPackHelperApp.exe"
#else
# define VBOX_EXTPACK_HELPER_NAME "VBoxExtPackHelperApp"
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Private extension pack data.
*/
{
public:
/** The extension pack descriptor (loaded from the XML, mostly). */
/** The file system object info of the XML file.
* This is for detecting changes and save time in refresh(). */
/** Whether it's usable or not. */
bool fUsable;
/** Why it is unusable. */
/** Where the extension pack is located. */
/** The file system object info of the extension pack directory.
* This is for detecting changes and save time in refresh(). */
/** The full path to the main module. */
/** The file system object info of the main module.
* This is used to determin whether to bother try reload it. */
/** The module handle of the main extension pack module. */
/** The helper callbacks for the extension pack. */
/** Pointer back to the extension pack object (for Hlp methods). */
/** The extension pack registration structure. */
/** The current context. */
/** Set if we've made the pfnVirtualBoxReady or pfnConsoleReady call. */
bool fMadeReadyCall;
};
/** List of extension packs. */
/**
* Private extension pack manager data.
*/
struct ExtPackManager::Data
{
/** The directory where the extension packs are installed. */
/** The directory where the extension packs can be dropped for automatic
* installation. */
/** The directory where the certificates this installation recognizes are
* stored. */
/** The list of installed extension packs. */
/** Pointer to the VirtualBox object, our parent. */
/** The current context. */
};
/**
* Called by ComObjPtr::createObject when creating the object.
*
* Just initialize the basic object state, do the rest in init().
*
* @returns S_OK.
*/
{
m = NULL;
return S_OK;
}
/**
* Initializes the extension pack by reading its file.
*
* @returns COM status code.
* @param a_enmContext The context we're in.
* @param a_pszName The name of the extension pack. This is also the
* name of the subdirector under @a a_pszParentDir
* where the extension pack is installed.
* @param a_pszParentDir The parent directory.
*/
HRESULT ExtPack::init(VBOXEXTPACKCTX a_enmContext, const char *a_pszName, const char *a_pszParentDir)
{
AutoInitSpan autoInitSpan(this);
static const VBOXEXTPACKHLP s_HlpTmpl =
{
/* u32Version = */ VBOXEXTPACKHLP_VERSION,
/* uVBoxFullVersion = */ VBOX_FULL_VERSION,
/* uVBoxVersionRevision = */ 0,
/* u32Padding = */ 0,
/* pszVBoxVersion = */ "",
/* u32EndMarker = */ VBOXEXTPACKHLP_VERSION
};
/*
* Figure out where we live and allocate + initialize our private data.
*/
char szDir[RTPATH_MAX];
m = new Data;
RT_ZERO(m->ObjInfoDesc);
m->fUsable = false;
m->strExtPackPath = szDir;
RT_ZERO(m->ObjInfoExtPack);
m->strMainModPath.setNull();
RT_ZERO(m->ObjInfoMainMod);
m->hMainMod = NIL_RTLDRMOD;
m->pThis = this;
m->enmContext = a_enmContext;
m->fMadeReadyCall = false;
/*
* Probe the extension pack (this code is shared with refresh()).
*/
probeAndLoad();
return S_OK;
}
/**
* COM cruft.
*/
void ExtPack::FinalRelease()
{
uninit();
}
/**
* Do the actual cleanup.
*/
{
/* Enclose the state transition Ready->InUninit->NotReady */
AutoUninitSpan autoUninitSpan(this);
{
if (m->hMainMod != NIL_RTLDRMOD)
{
RTLdrClose(m->hMainMod);
m->hMainMod = NIL_RTLDRMOD;
}
VBoxExtPackFreeDesc(&m->Desc);
delete m;
m = NULL;
}
}
/**
* Calls the installed hook.
*
* @returns true if we left the lock, false if we didn't.
* @param a_pVirtualBox The VirtualBox interface.
* @param a_pLock The write lock held by the caller.
*/
{
if ( m != NULL
&& m->hMainMod != NIL_RTLDRMOD)
{
if (m->pReg->pfnInstalled)
{
return true;
}
}
return false;
}
/**
* Calls the uninstall hook and closes the module.
*
* @returns S_OK or COM error status with error information.
* @param a_pVirtualBox The VirtualBox interface.
* @param a_fForcedRemoval When set, we'll ignore complaints from the
* uninstall hook.
* @remarks The caller holds the manager's write lock, not released.
*/
{
if ( m != NULL
&& m->hMainMod != NIL_RTLDRMOD)
{
{
if (RT_FAILURE(vrc))
{
if (!a_fForcedRemoval)
}
}
{
RTLdrClose(m->hMainMod);
m->hMainMod = NIL_RTLDRMOD;
}
}
return hrc;
}
/**
* Calls the pfnVirtualBoxReady hook.
*
* @returns true if we left the lock, false if we didn't.
* @param a_pVirtualBox The VirtualBox interface.
* @param a_pLock The write lock held by the caller.
*/
{
if ( m != NULL
&& m->fUsable
&& !m->fMadeReadyCall)
{
m->fMadeReadyCall = true;
if (m->pReg->pfnVirtualBoxReady)
{
return true;
}
}
return false;
}
/**
* Calls the pfnConsoleReady hook.
*
* @returns true if we left the lock, false if we didn't.
* @param a_pConsole The Console interface.
* @param a_pLock The write lock held by the caller.
*/
{
if ( m != NULL
&& m->fUsable
&& !m->fMadeReadyCall)
{
m->fMadeReadyCall = true;
if (m->pReg->pfnConsoleReady)
{
return true;
}
}
return false;
}
/**
* Calls the pfnVMCreate hook.
*
* @returns true if we left the lock, false if we didn't.
* @param a_pVirtualBox The VirtualBox interface.
* @param a_pMachine The machine interface of the new VM.
* @param a_pLock The write lock held by the caller.
*/
bool ExtPack::callVmCreatedHook(IVirtualBox *a_pVirtualBox, IMachine *a_pMachine, AutoWriteLock *a_pLock)
{
if ( m != NULL
&& m->fUsable)
{
if (m->pReg->pfnVMCreated)
{
return true;
}
}
return false;
}
/**
* Calls the pfnVMConfigureVMM hook.
*
* @returns true if we left the lock, false if we didn't.
* @param a_pConsole The console interface.
* @param a_pVM The VM handle.
* @param a_pLock The write lock held by the caller.
* @param a_pvrc Where to return the status code of the
* callback. This is always set. LogRel is
* called on if a failure status is returned.
*/
bool ExtPack::callVmConfigureVmmHook(IConsole *a_pConsole, PVM a_pVM, AutoWriteLock *a_pLock, int *a_pvrc)
{
*a_pvrc = VINF_SUCCESS;
if ( m != NULL
&& m->fUsable)
{
if (m->pReg->pfnVMConfigureVMM)
{
if (RT_FAILURE(vrc))
return true;
}
}
return false;
}
/**
* Calls the pfnVMPowerOn hook.
*
* @returns true if we left the lock, false if we didn't.
* @param a_pConsole The console interface.
* @param a_pVM The VM handle.
* @param a_pLock The write lock held by the caller.
* @param a_pvrc Where to return the status code of the
* callback. This is always set. LogRel is
* called on if a failure status is returned.
*/
bool ExtPack::callVmPowerOnHook(IConsole *a_pConsole, PVM a_pVM, AutoWriteLock *a_pLock, int *a_pvrc)
{
*a_pvrc = VINF_SUCCESS;
if ( m != NULL
&& m->fUsable)
{
if (m->pReg->pfnVMPowerOn)
{
if (RT_FAILURE(vrc))
return true;
}
}
return false;
}
/**
* Calls the pfnVMPowerOff hook.
*
* @returns true if we left the lock, false if we didn't.
* @param a_pConsole The console interface.
* @param a_pVM The VM handle.
* @param a_pLock The write lock held by the caller.
*/
{
if ( m != NULL
&& m->fUsable)
{
if (m->pReg->pfnVMPowerOff)
{
return true;
}
}
return false;
}
/**
* Check if the extension pack is usable and has an VRDE module.
*
* @returns S_OK or COM error status with error information.
*
* @remarks Caller holds the extension manager lock for reading, no locking
* necessary.
*/
{
if ( m != NULL
&& m->fUsable)
{
else
hrc = setError(E_FAIL, tr("The extension pack '%s' does not include a VRDE module"), m->Desc.strName.c_str());
}
else
return hrc;
}
/**
* Same as checkVrde(), except that it also resolves the path to the module.
*
* @returns S_OK or COM error status with error information.
* @param a_pstrVrdeLibrary Where to return the path on success.
*
* @remarks Caller holds the extension manager lock for reading, no locking
* necessary.
*/
{
{
else
}
return hrc;
}
/**
* Check if this extension pack wishes to be the default VRDE provider.
*
* @returns @c true if it wants to and it is in a usable state, otherwise
* @c false.
*
* @remarks Caller holds the extension manager lock for reading, no locking
* necessary.
*/
bool ExtPack::wantsToBeDefaultVrde(void) const
{
return m->fUsable
}
/**
* Refreshes the extension pack state.
*
* This is called by the manager so that the on disk changes are picked up.
*
* @returns S_OK or COM error status with error information.
*
* @param a_pfCanDelete Optional can-delete-this-object output indicator.
*
* @remarks Caller holds the extension manager lock for writing.
* @remarks Only called in VBoxSVC.
*/
{
if (a_pfCanDelete)
*a_pfCanDelete = false;
/*
* Has the module been deleted?
*/
int vrc = RTPathQueryInfoEx(m->strExtPackPath.c_str(), &ObjInfoExtPack, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
if ( RT_FAILURE(vrc)
{
if (a_pfCanDelete)
*a_pfCanDelete = true;
return S_OK;
}
/*
* We've got a directory, so try query file system object info for the
* files we are interested in as well.
*/
char szDescFilePath[RTPATH_MAX];
vrc = RTPathJoin(szDescFilePath, sizeof(szDescFilePath), m->strExtPackPath.c_str(), VBOX_EXTPACK_DESCRIPTION_NAME);
if (RT_SUCCESS(vrc))
if (RT_FAILURE(vrc))
if (m->strMainModPath.isNotEmpty())
vrc = RTPathQueryInfoEx(m->strMainModPath.c_str(), &ObjInfoMainMod, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
/*
* If we have a usable module already, just verify that things haven't
* changed since we loaded it.
*/
if (m->fUsable)
{
/** @todo not important, so it can wait. */
}
/*
* Ok, it is currently not usable. If anything has changed since last time
* reprobe the extension pack.
*/
probeAndLoad();
return S_OK;
}
/**
* Probes the extension pack, loading the main dll and calling its registration
* entry point.
*
* This updates the state accordingly, the strWhyUnusable and fUnusable members
* being the most important ones.
*/
void ExtPack::probeAndLoad(void)
{
m->fUsable = false;
m->fMadeReadyCall = false;
/*
* Query the file system info for the extension pack directory. This and
* all other file system info we save is for the benefit of refresh().
*/
int vrc = RTPathQueryInfoEx(m->strExtPackPath.c_str(), &m->ObjInfoExtPack, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
if (RT_FAILURE(vrc))
{
m->strWhyUnusable.printf(tr("RTPathQueryInfoEx on '%s' failed: %Rrc"), m->strExtPackPath.c_str(), vrc);
return;
}
{
m->strWhyUnusable.printf(tr("'%s' is a symbolic link, this is not allowed"), m->strExtPackPath.c_str(), vrc);
m->strWhyUnusable.printf(tr("'%s' is a symbolic file, not a directory"), m->strExtPackPath.c_str(), vrc);
else
m->strWhyUnusable.printf(tr("'%s' is not a directory (fMode=%#x)"), m->strExtPackPath.c_str(), m->ObjInfoExtPack.Attr.fMode);
return;
}
char szErr[2048];
vrc = SUPR3HardenedVerifyDir(m->strExtPackPath.c_str(), true /*fRecursive*/, true /*fCheckFiles*/, szErr, sizeof(szErr));
if (RT_FAILURE(vrc))
{
return;
}
/*
* Read the description file.
*/
iprt::MiniString *pStrLoadErr = VBoxExtPackLoadDesc(m->strExtPackPath.c_str(), &m->Desc, &m->ObjInfoDesc);
if (pStrLoadErr != NULL)
{
delete pStrLoadErr;
return;
}
/*
* Make sure the XML name and directory matches.
*/
{
m->strWhyUnusable.printf(tr("The description name ('%s') and directory name ('%s') does not match"),
return;
}
/*
* Load the main DLL and call the predefined entry point.
*/
bool fIsNative;
{
m->strWhyUnusable.printf(tr("Failed to locate the main module ('%s')"), m->Desc.strMainModule.c_str());
return;
}
if (RT_FAILURE(vrc))
{
return;
}
if (fIsNative)
{
if (RT_FAILURE(vrc))
{
m->hMainMod = NIL_RTLDRMOD;
return;
}
}
else
{
return;
}
/*
* Resolve the predefined entry point.
*/
if (RT_SUCCESS(vrc))
{
if ( RT_SUCCESS(vrc)
&& szErr[0] == '\0'
{
{
)
{
/*
* We're good!
*/
m->fUsable = true;
m->strWhyUnusable.setNull();
return;
}
}
else
}
else
{
}
}
else
RTLdrClose(m->hMainMod);
m->hMainMod = NIL_RTLDRMOD;
}
/**
* Finds a module.
*
* @returns true if found, false if not.
* @param a_pszName The module base name (no extension).
* @param a_pszExt The extension. If NULL we use default
* extensions.
* @param a_enmKind The kind of module to locate.
* @param a_pStrFound Where to return the path to the module we've
* found.
* @param a_pfNative Where to return whether this is a native module
* or an agnostic one. Optional.
* @param a_pObjInfo Where to return the file system object info for
* the module. Optional.
*/
{
/*
* Try the native path first.
*/
char szPath[RTPATH_MAX];
AssertLogRelRCReturn(vrc, false);
AssertLogRelRCReturn(vrc, false);
if (!a_pszExt)
{
const char *pszDefExt;
switch (a_enmKind)
{
default:
AssertFailedReturn(false);
}
AssertLogRelRCReturn(vrc, false);
}
if (!a_pObjInfo)
a_pObjInfo = &ObjInfo;
{
if (a_pfNative)
*a_pfNative = true;
*a_pStrFound = szPath;
return true;
}
/*
* Try the platform agnostic modules.
*/
/* gcc.x86/module.rel */
char szSubDir[32];
AssertLogRelRCReturn(vrc, false);
AssertLogRelRCReturn(vrc, false);
if (!a_pszExt)
{
AssertLogRelRCReturn(vrc, false);
}
{
if (a_pfNative)
*a_pfNative = false;
*a_pStrFound = szPath;
return true;
}
/* x86/module.rel */
AssertLogRelRCReturn(vrc, false);
AssertLogRelRCReturn(vrc, false);
if (!a_pszExt)
{
AssertLogRelRCReturn(vrc, false);
}
{
if (a_pfNative)
*a_pfNative = false;
*a_pStrFound = szPath;
return true;
}
return false;
}
/**
* Compares two file system object info structures.
*
* @returns true if equal, false if not.
* @param pObjInfo1 The first.
* @param pObjInfo2 The second.
* @todo IPRT should do this, really.
*/
{
return false;
return false;
return false;
return false;
return false;
{
{
case RTFSOBJATTRADD_UNIX:
return false;
return false;
return false;
return false;
return false;
break;
default:
break;
}
}
return true;
}
/**
* @interface_method_impl{VBOXEXTPACKHLP,pfnFindModule}
*/
/*static*/ DECLCALLBACK(int)
ExtPack::hlpFindModule(PCVBOXEXTPACKHLP pHlp, const char *pszName, const char *pszExt, VBOXEXTPACKMODKIND enmKind,
{
/*
* Validate the input and get our bearings.
*/
AssertReturn(enmKind > VBOXEXTPACKMODKIND_INVALID && enmKind < VBOXEXTPACKMODKIND_END, VERR_INVALID_PARAMETER);
/*
* This is just a wrapper around findModule.
*/
return VERR_FILE_NOT_FOUND;
}
/*static*/ DECLCALLBACK(int)
ExtPack::hlpGetFilePath(PCVBOXEXTPACKHLP pHlp, const char *pszFilename, char *pszPath, size_t cbPath)
{
/*
* Validate the input and get our bearings.
*/
/*
* This is a simple RTPathJoin, no checking if things exists or anything.
*/
if (RT_FAILURE(vrc))
return vrc;
}
{
/*
* Validate the input and get our bearings.
*/
return pThis->m->enmContext;
}
/*static*/ DECLCALLBACK(int)
{
/*
* Validate the input and get our bearings.
*/
return VERR_NOT_IMPLEMENTED;
}
{
AutoCaller autoCaller(this);
{
}
return hrc;
}
{
AutoCaller autoCaller(this);
{
}
return hrc;
}
{
AutoCaller autoCaller(this);
{
}
return hrc;
}
{
AutoCaller autoCaller(this);
return hrc;
}
{
AutoCaller autoCaller(this);
{
}
return hrc;
}
{
/** @todo implement plug-ins. */
#ifdef VBOX_WITH_XPCOM
#endif
}
{
AutoCaller autoCaller(this);
*a_pfUsable = m->fUsable;
return hrc;
}
{
AutoCaller autoCaller(this);
return hrc;
}
{
AutoCaller autoCaller(this);
{
if ( m->pReg
&& m->pReg->pfnQueryObject)
{
if (pvUnknown)
else
hrc = E_NOINTERFACE;
}
else
hrc = E_NOINTERFACE;
}
return hrc;
}
/**
* Called by ComObjPtr::createObject when creating the object.
*
* Just initialize the basic object state, do the rest in init().
*
* @returns S_OK.
*/
{
m = NULL;
return S_OK;
}
/**
* Initializes the extension pack manager.
*
* @returns COM status code.
* @param a_pVirtualBox Pointer to the VirtualBox object.
* @param a_pszDropZoneDir The path to the drop zone directory.
* @param a_fCheckDropZone Whether to check the drop zone for new
* extensions or not. Only VBoxSVC does this
* and then only when wanted.
* @param a_enmContext The context we're in.
*/
HRESULT ExtPackManager::init(VirtualBox *a_pVirtualBox, const char *a_pszDropZoneDir, bool a_fCheckDropZone,
{
AutoInitSpan autoInitSpan(this);
/*
* Figure some stuff out before creating the instance data.
*/
char szBaseDir[RTPATH_MAX];
char szCertificatDir[RTPATH_MAX];
/*
* Allocate and initialize the instance data.
*/
m = new Data;
m->strBaseDir = szBaseDir;
m->pVirtualBox = a_pVirtualBox;
m->enmContext = a_enmContext;
/*
* Go looking for extensions. The RTDirOpen may fail if nothing has been
* installed yet, or if root is paranoid and has revoked our access to them.
*
* We ASSUME that there are no files, directories or stuff in the directory
* that exceed the max name length in RTDIRENTRYEX.
*/
if (RT_SUCCESS(vrc))
{
for (;;)
{
if (RT_FAILURE(vrc))
{
break;
}
{
/*
* All directories are extensions, the shall be nothing but
* extensions in this subdirectory.
*/
}
}
}
/* else: ignore, the directory probably does not exist or something. */
/*
* Look for things in the drop zone.
*/
return hrc;
}
/**
* COM cruft.
*/
void ExtPackManager::FinalRelease()
{
uninit();
}
/**
* Do the actual cleanup.
*/
void ExtPackManager::uninit()
{
/* Enclose the state transition Ready->InUninit->NotReady */
AutoUninitSpan autoUninitSpan(this);
{
delete m;
m = NULL;
}
}
STDMETHODIMP ExtPackManager::COMGETTER(InstalledExtPacks)(ComSafeArrayOut(IExtPack *, a_paExtPacks))
{
AutoCaller autoCaller(this);
{
}
return hrc;
}
{
AutoCaller autoCaller(this);
{
if (!ptrExtPack.isNull())
else
}
return hrc;
}
{
AutoCaller autoCaller(this);
{
/*
* Check that the file exists and that we can access it.
*/
{
int vrc = RTFileOpen(&hFile, strTarball.c_str(), RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN);
if (RT_SUCCESS(vrc))
{
if ( RT_SUCCESS(vrc)
{
/*
* Derive the name of the extension pack from the file
* name. Certain restrictions are here placed on the
* tarball name.
*/
if (pStrName)
{
/*
* Refresh the data we have on the extension pack as it
* may be made stale by direct meddling or some other user.
*/
{
/*
* Run the set-uid-to-root binary that performs the actual
* installation. Then create an object for the packet (we
* do this even on failure, to be on the safe side).
*/
char szTarballFd[64];
"--tarball-fd", &szTarballFd[0],
NULL);
{
{
}
}
else
{
}
}
tr("Extension pack '%s' is already installed."
" In case of a reinstallation, please uninstall it first"),
delete pStrName;
}
else
}
else if (RT_SUCCESS(vrc))
else
}
else
}
else
/*
* Do VirtualBoxReady callbacks now for any freshly installed
* extension pack (old ones will not be called).
*/
if (m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON)
{
}
}
return hrc;
}
{
AutoCaller autoCaller(this);
{
/*
* Refresh the data we have on the extension pack as it may be made
* stale by direct meddling or some other user.
*/
{
if (!pExtPack)
{
LogRel(("ExtPackManager: Extension pack '%s' is not installed, so nothing to uninstall.\n", strName.c_str()));
}
else
{
/*
* Call the uninstall hook and unload the main dll.
*/
{
/*
* Run the set-uid-to-root binary that performs the
* uninstallation. Then refresh the object.
*
* This refresh is theorically subject to races, but it's of
* the don't-do-that variety.
*/
pszForcedOpt, /* Last as it may be NULL. */
NULL);
{
{
if (!pExtPack)
else
tr("Uninstall extension pack '%s' failed under mysterious circumstances"),
}
}
else
{
}
}
}
}
/*
* Do VirtualBoxReady callbacks now for any freshly installed
* extension pack (old ones will not be called).
*/
if (m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON)
{
}
}
return hrc;
}
{
AutoCaller autoCaller(this);
{
/*
* Run the set-uid-to-root binary that performs the cleanup.
*
* Take the write lock to prevent conflicts with other calls to this
* VBoxSVC instance.
*/
NULL);
}
return hrc;
}
STDMETHODIMP ExtPackManager::QueryAllPlugInsForFrontend(IN_BSTR a_bstrFrontend, ComSafeArrayOut(BSTR, a_pabstrPlugInModules))
{
AutoCaller autoCaller(this);
{
/** @todo implement plug-ins */
}
return hrc;
}
/**
* Runs the helper application that does the privileged operations.
*
* @returns S_OK or a failure status with error information set.
* @param a_pszCommand The command to execute.
* @param ... The argument strings that goes along with the
* command. Maximum is about 16. Terminated by a
* NULL.
*/
{
/*
* Calculate the path to the helper application.
*/
char szExecName[RTPATH_MAX];
/*
* Convert the variable argument list to a RTProcCreate argument vector.
*/
const char *apszArgs[20];
unsigned cArgs = 0;
const char *pszLastArg;
do
{
} while (pszLastArg != NULL);
LogRel(("\n"));
/*
* Create a PIPE which we attach to stderr so that we can read the error
* message on failure and report it back to the caller.
*/
/*
* Spawn the process.
*/
0 /*fFlags*/,
NULL /*phStdIn*/,
NULL /*phStdOut*/,
NULL /*pszAsUser*/,
NULL /*pszPassword*/,
&hProcess);
if (RT_SUCCESS(vrc))
{
/*
* Read the pipe output until the process completes.
*/
size_t cbStdErrBuf = 0;
size_t offStdErrBuf = 0;
char *pszStdErrBuf = NULL;
do
{
/*
* Service the pipe. Block waiting for output or the pipe breaking
* when the process terminates.
*/
if (hPipeR != NIL_RTPIPE)
{
if (RT_SUCCESS(vrc))
{
/* grow the buffer? */
if ( cbBufReq > cbStdErrBuf
{
if (pvNew)
{
pszStdErrBuf = (char *)pvNew;
cbStdErrBuf = cbNew;
}
}
/* append if we've got room. */
if (cbBufReq <= cbStdErrBuf)
{
}
}
else
{
hPipeR = NIL_RTPIPE;
}
}
/*
* Service the process. Block if we have no pipe.
*/
if (hProcess != NIL_RTPROCESS)
{
&ProcStatus);
if (RT_SUCCESS(vrc))
else
}
} while ( hPipeR != NIL_RTPIPE
|| hProcess != NIL_RTPROCESS);
/*
* Compose the status code and, on failure, error message.
*/
LogRel(("ExtPack: enmReason=%d iStatus=%d stderr='%s'\n",
&& ProcStatus.iStatus == 0
&& offStdErrBuf == 0)
{
}
else
}
else
hrc = setError(VBOX_E_IPRT_ERROR, tr("Failed to launch the helper application '%s' (%Rrc)"), szExecName, vrc);
return hrc;
}
/**
* Finds an installed extension pack.
*
* @returns Pointer to the extension pack if found, NULL if not. (No reference
* counting problem here since the caller must be holding the lock.)
* @param a_pszName The name of the extension pack.
*/
{
it++)
{
if ( pExtPackData
return (*it);
}
return NULL;
}
/**
* Removes an installed extension pack from the internal list.
*
* The package is expected to exist!
*
* @param a_pszName The name of the extension pack.
*/
{
it++)
{
if ( pExtPackData
{
return;
}
}
}
/**
* Refreshes the specified extension pack.
*
* This may remove the extension pack from the list, so any non-smart pointers
* to the extension pack object may become invalid.
*
* @returns S_OK and *ppExtPack on success, COM status code and error message
* on failure.
*
* @param a_pszName The extension to update..
* @param a_fUnsuableIsError If @c true, report an unusable extension pack
* as an error.
* @param a_ppExtPack Where to store the pointer to the extension
* pack of it is still around after the refresh.
* This is optional.
*
* @remarks Caller holds the extension manager lock.
* @remarks Only called in VBoxSVC.
*/
HRESULT ExtPackManager::refreshExtPack(const char *a_pszName, bool a_fUnsuableIsError, ExtPack **a_ppExtPack)
{
if (pExtPack)
{
/*
* Refresh existing object.
*/
bool fCanDelete;
{
if (fCanDelete)
{
}
}
}
else
{
/*
* Does the dir exist? Make some special effort to deal with case
* sensitivie file systems (a_pszName is case insensitive).
*/
char szDir[RTPATH_MAX];
if (!fExists)
{
if (RT_SUCCESS(vrc))
{
for (;;)
{
if (RT_FAILURE(vrc))
{
break;
}
{
/*
* The installed extension pack has a uses different case.
* Update the name and directory variables.
*/
vrc = RTPathJoin(szDir, sizeof(szDir), m->strBaseDir.c_str(), Entry.szName); /* not really necessary */
fExists = true;
break;
}
}
}
}
if (fExists)
{
/*
* We've got something, create a new extension pack object for it.
*/
{
if (NewExtPack->m->fUsable)
else
LogRel(("ExtPackManager: Found bad extension pack '%s': %s\n",
}
}
else
}
/*
* Report error if not usable, if that is desired.
*/
&& pExtPack
if (a_ppExtPack)
*a_ppExtPack = pExtPack;
return hrc;
}
/**
* Processes anything new in the drop zone.
*/
void ExtPackManager::processDropZone(void)
{
AutoCaller autoCaller(this);
return;
if (m->strDropZoneDir.isEmpty())
return;
if (RT_FAILURE(vrc))
return;
for (;;)
{
if (RT_FAILURE(vrc))
{
break;
}
/*
* We're looking for files with the right extension. Symbolic links
* will be ignored.
*/
{
/* We create (and check for) a blocker file to prevent this
extension pack from being installed more than once. */
char szPath[RTPATH_MAX];
if (RT_SUCCESS(vrc))
if (RT_SUCCESS(vrc))
{
if (RT_SUCCESS(vrc))
{
/* Construct the full path to the extension pack and invoke
the Install method to install it. Write errors to the
done file. */
else
{
if (Info.isFullAvailable())
"%ls\n"
"Details: code %Rhrc (%#RX32), component %ls, interface %ls, callee %ls\n"
,
else
}
}
}
}
} /* foreach dir entry */
}
/**
* Calls the pfnVirtualBoxReady hook for all working extension packs.
*
* @remarks The caller must not hold any locks.
*/
void ExtPackManager::callAllVirtualBoxReadyHooks(void)
{
AutoCaller autoCaller(this);
return;
/* advancing below */)
{
else
it++;
}
}
/**
* Calls the pfnConsoleReady hook for all working extension packs.
*
* @param a_pConsole The console interface.
* @remarks The caller must not hold any locks.
*/
{
AutoCaller autoCaller(this);
return;
/* advancing below */)
{
else
it++;
}
}
/**
* Calls the pfnVMCreated hook for all working extension packs.
*
* @param a_pMachine The machine interface of the new VM.
*/
{
AutoCaller autoCaller(this);
return;
}
/**
* Calls the pfnVMConfigureVMM hook for all working extension packs.
*
* @returns VBox status code. Stops on the first failure, expecting the caller
* to signal this to the caller of the CFGM constructor.
* @param a_pConsole The console interface for the VM.
* @param a_pVM The VM handle.
*/
{
AutoCaller autoCaller(this);
{
int vrc;
if (RT_FAILURE(vrc))
return vrc;
}
return VINF_SUCCESS;
}
/**
* Calls the pfnVMPowerOn hook for all working extension packs.
*
* @returns VBox status code. Stops on the first failure, expecting the caller
* to not power on the VM.
* @param a_pConsole The console interface for the VM.
* @param a_pVM The VM handle.
*/
{
AutoCaller autoCaller(this);
{
int vrc;
if (RT_FAILURE(vrc))
return vrc;
}
return VINF_SUCCESS;
}
/**
* Calls the pfnVMPowerOff hook for all working extension packs.
*
* @param a_pConsole The console interface for the VM.
* @param a_pVM The VM handle. Can be NULL.
*/
{
AutoCaller autoCaller(this);
return;
}
/**
* Checks that the specified extension pack contains a VRDE module and that it
* is shipshape.
*
* @returns S_OK if ok, appropriate failure status code with details.
* @param a_pstrExtPack The name of the extension pack.
*/
{
AutoCaller autoCaller(this);
{
if (pExtPack)
else
hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"), a_pstrExtPack->c_str());
}
return hrc;
}
/**
* Gets the full path to the VRDE library of the specified extension pack.
*
* This will do extacly the same as checkVrdeExtPack and then resolve the
* library path.
*
* @returns S_OK if a path is returned, COM error status and message return if
* not.
* @param a_pstrExtPack The extension pack.
* @param a_pstrVrdeLibrary Where to return the path.
*/
int ExtPackManager::getVrdeLibraryPathForExtPack(Utf8Str const *a_pstrExtPack, Utf8Str *a_pstrVrdeLibrary)
{
AutoCaller autoCaller(this);
{
if (pExtPack)
else
hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No extension pack by the name '%s' was found"), a_pstrExtPack->c_str());
}
return hrc;
}
/**
* Gets the name of the default VRDE extension pack.
*
* @returns S_OK or some COM error status on red tape failure.
* @param a_pstrExtPack Where to return the extension pack name. Returns
* empty if no extension pack wishes to be the default
* VRDP provider.
*/
{
a_pstrExtPack->setNull();
AutoCaller autoCaller(this);
{
it++)
{
if ((*it)->wantsToBeDefaultVrde())
{
break;
}
}
}
return hrc;
}
/* vi: set tabstop=4 shiftwidth=4 expandtab: */