/* $Id$ */
/** @file
* MS COM / XPCOM Abstraction Layer
*/
/*
* Copyright (C) 2005-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#if !defined (VBOX_WITH_XPCOM)
# include <objbase.h>
#else /* !defined (VBOX_WITH_XPCOM) */
# include <stdlib.h>
# include <nsCOMPtr.h>
# include <nsIServiceManagerUtils.h>
# include <nsIComponentManager.h>
# include <ipcIService.h>
# include <ipcCID.h>
# include <ipcIDConnectService.h>
# include <nsIInterfaceInfo.h>
# include <nsIInterfaceInfoManager.h>
// official XPCOM headers don't define it yet
#define IPC_DCONNECTSERVICE_CONTRACTID \
"@mozilla.org/ipc/dconnect-service;1"
#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 <package-generated.h>
#include <iprt/buildconfig.h>
#include <iprt/param.h>
#include <iprt/path.h>
#include <iprt/dir.h>
#include <iprt/env.h>
#include <iprt/string.h>
#include <iprt/system.h>
#include <iprt/process.h>
#include <VBox/err.h>
#include <VBox/version.h>
#if !defined(RT_OS_DARWIN) && !defined(RT_OS_WINDOWS)
char 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 apcszUserHome[] =
#ifdef RT_OS_DARWIN
{ "Library/VirtualBox" };
#elif defined RT_OS_WINDOWS
{ ".VirtualBox" };
#else
{ ".VirtualBox", szXdgConfigHome };
#endif
#include "Logging.h"
namespace com
{
void GetInterfaceNameByIID(const GUID &aIID, BSTR *aName)
{
Assert(aName);
if (!aName)
return;
*aName = NULL;
#if !defined(VBOX_WITH_XPCOM)
LONG rc;
LPOLESTR iidStr = NULL;
if (StringFromIID(aIID, &iidStr) == S_OK)
{
HKEY ifaceKey;
rc = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Interface",
0, KEY_QUERY_VALUE, &ifaceKey);
if (rc == ERROR_SUCCESS)
{
HKEY iidKey;
rc = RegOpenKeyExW(ifaceKey, iidStr, 0, KEY_QUERY_VALUE, &iidKey);
if (rc == ERROR_SUCCESS)
{
/* determine the size and type */
DWORD sz, type;
rc = RegQueryValueExW(iidKey, NULL, NULL, &type, NULL, &sz);
if (rc == ERROR_SUCCESS && type == REG_SZ)
{
/* query the value to BSTR */
*aName = SysAllocStringLen(NULL, (sz + 1) / sizeof(TCHAR) + 1);
rc = RegQueryValueExW(iidKey, NULL, NULL, NULL, (LPBYTE) *aName, &sz);
if (rc != ERROR_SUCCESS)
{
SysFreeString(*aName);
aName = NULL;
}
}
RegCloseKey(iidKey);
}
RegCloseKey(ifaceKey);
}
CoTaskMemFree(iidStr);
}
#else /* !defined (VBOX_WITH_XPCOM) */
nsresult rv;
nsCOMPtr<nsIInterfaceInfoManager> iim =
do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIInterfaceInfo> 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 rc;
nsCOMPtr<ipcIService> ipcServ = do_GetService(IPC_SERVICE_CONTRACTID, &rc);
if (SUCCEEDED(rc))
{
PRUint32 serverID = 0;
rc = ipcServ->ResolveClientName(serverName, &serverID);
if (SUCCEEDED (rc))
{
nsCOMPtr<ipcIDConnectService> dconServ = do_GetService(IPC_DCONNECTSERVICE_CONTRACTID, &rc);
if (SUCCEEDED(rc))
rc = dconServ->CreateInstance(serverID,
clsid,
id,
ppobj);
}
}
return rc;
}
HRESULT GlueCreateInstance(const CLSID &clsid,
const nsIID &id,
void** ppobj)
{
nsCOMPtr<nsIComponentManager> manager;
HRESULT rc = NS_GetComponentManager(getter_AddRefs(manager));
if (SUCCEEDED(rc))
rc = manager->CreateInstance(clsid,
nsnull,
id,
ppobj);
return rc;
}
#endif // VBOX_WITH_XPCOM
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)
const char *pcszConfigHome = RTEnvGet("XDG_CONFIG_HOME");
if (pcszConfigHome && pcszConfigHome[0])
{
vrc = RTStrCopy(szXdgConfigHome,
sizeof(szXdgConfigHome),
pcszConfigHome);
if (RT_SUCCESS(vrc))
vrc = RTPathAppend(szXdgConfigHome,
sizeof(szXdgConfigHome),
"VirtualBox");
}
else
vrc = RTStrCopy(szXdgConfigHome,
sizeof(szXdgConfigHome),
".config/VirtualBox");
#endif
for (unsigned i = 0; i < RT_ELEMENTS(apcszUserHome); ++i)
{
vrc = composeHomePath(aDir, aDirLen, 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;
}
static const char *g_pszLogEntity = NULL;
static 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);
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 total, %lluMB available\n",
cbHostRam / _1M, cbHostRamAvail / _1M);
/* 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,
char *pszError, size_t cbError)
{
Assert(cbError >= RTPATH_MAX + 128);
/* 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, fFlags, pcszGroupSettings,
pcszEnvVarBase, RT_ELEMENTS(s_apszGroups), s_apszGroups, fDestFlags,
vboxHeaderFooter, cHistory, uHistoryFileSize, uHistoryFileTime,
pszError, cbError,
pcszLogFile ? "%s" : NULL, pcszLogFile);
if (RT_SUCCESS(vrc))
{
/* make sure that we don't flood logfiles */
RTLogSetGroupLimit(pReleaseLogger, cMaxEntriesPerGroup);
/* explicitly flush the log, to have some info when buffering */
RTLogFlush(pReleaseLogger);
/* register this logger as the release logger */
RTLogRelSetDefaultInstance(pReleaseLogger);
}
return vrc;
}
/* static */
const Guid Guid::Empty; /* default ctor is OK */
#if defined (VBOX_WITH_XPCOM)
/* static */
const nsID *SafeGUIDArray::nsIDRef::Empty = (const nsID *)Guid::Empty.raw();
#endif /* (VBOX_WITH_XPCOM) */
/**
* Used by ComPtr and friends to log details about reference counting.
* @param pcszFormat
*/
void LogRef(const char *pcszFormat, ...)
{
char *pszNewMsg;
va_list args;
va_start(args, pcszFormat);
RTStrAPrintfV(&pszNewMsg, pcszFormat, args);
LogDJ((pszNewMsg));
RTStrFree(pszNewMsg);
va_end(args);
}
} /* namespace com */