ExtPackManagerImpl.cpp revision 8d82d07fc2f7234b1e5a3ba544e1086a5a7a7c5f
/* $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 "AutoCaller.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** @name VBOX_EXTPACK_HELPER_NAME
* The name of the utility program 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 program.
*/
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
# define VBOX_EXTPACK_HELPER_NAME "VBoxExtPackHelper.exe"
#else
# define VBOX_EXTPACK_HELPER_NAME "VBoxExtPackHelper"
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Private extension pack data.
*/
{
/** @name IExtPack Attributes.
* @{ */
/** The name. */
/** The version string. */
/** The revision. */
/** Whether it's usable or not. */
bool fUsable;
/** Why it is unusable. */
/** @} */
/** Where the extension pack is located. */
/** The module handle of the main extension pack module. */
};
/** List of extension packs. */
/**
* Private extension pack manager data.
*/
struct ExtPackManager::Data
{
/** Where the directory where the extension packs are installed. */
/** The list of installed extension packs. */
};
/**
* 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_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.
*/
{
/*
* Figure out where we live and allocate+initialize our private data.
*/
char szDir[RTPATH_MAX];
m = new Data;
m->strVersion = "";
m->uRevision = 0;
m->fUsable = false;
m->hMainMod = NIL_RTLDRMOD;
/*
* Read the description file.
*/
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;
}
delete m;
m = NULL;
}
}
{
AutoCaller autoCaller(this);
return hrc;
}
{
AutoCaller autoCaller(this);
return hrc;
}
{
AutoCaller autoCaller(this);
*a_puRevision = m->uRevision;
return hrc;
}
{
AutoCaller autoCaller(this);
*a_pfUsable = m->fUsable;
return hrc;
}
{
AutoCaller autoCaller(this);
return hrc;
}
void *ExtPack::getCallbackTable()
{
return NULL;
}
/**
* 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 pfCanDelete Optional can-delete-this-object output indicator.
*/
{
if (pfCanDelete)
*pfCanDelete = false;
return S_OK;
}
/**
* 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.
*/
{
/*
* Figure some stuff out before creating the instance data.
*/
char szBasePath[RTPATH_MAX];
/*
* Allocate and initialize the instance data.
*/
m = new Data;
m->strBasePath = szBasePath;
/*
* 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_FAILURE(vrc))
return S_OK;
for (;;)
{
if (RT_FAILURE(vrc))
{
break;
}
{
/*
* All directories are extensions, the shall be nothing but
* extensions in this subdirectory.
*/
}
}
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);
{
/** @todo do unload notifications */
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.
*
* RESTRICTION: The name can only contain english alphabet
* charactes, decimal digits and space.
* Impose a max length of 64 chars.
*/
if (pszName)
{
pszEnd++;
{
*pszEnd = '\0';
/*
* 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];
"--name", pszName,
"--tarball-fd", &szTarballFd[0],
NULL);
{
}
else
{
}
}
tr("Extension pack '%s' is already installed."
" In case of a reinstallation, please uninstall it first"),
pszName);
}
else
}
else
hrc = E_OUTOFMEMORY;
}
else if (RT_SUCCESS(vrc))
else
}
else
}
else
}
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
{
/*
* 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.
*/
NULL);
{
{
if (!pExtPack)
else
tr("Uninstall extension pack '%s' failed under mysterious circumstances"),
}
}
else
{
}
}
}
}
return hrc;
}
/**
* Runs the helper program 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 program.
*/
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.
*/
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->strBasePath.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;
}
{
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
/* vi: set tabstop=4 shiftwidth=4 expandtab: */