/* $Id: HostImpl.cpp 38406 2011-08-10 15:40:47Z vboxsync $ */ /** @file * VirtualBox COM class implementation: Host */ /* * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS // for some reason Windows burns in sdk\...\winsock.h if this isn't included first #include "VBox/com/ptr.h" #include "HostImpl.h" #ifdef VBOX_WITH_USB # include "HostUSBDeviceImpl.h" # include "USBDeviceFilterImpl.h" # include "USBProxyService.h" #endif // VBOX_WITH_USB #include "HostNetworkInterfaceImpl.h" #include "MachineImpl.h" #include "AutoCaller.h" #include "Logging.h" #include "Performance.h" #include "MediumImpl.h" #include "HostPower.h" #if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) # include #endif #if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) # include #endif #ifdef VBOX_WITH_RESOURCE_USAGE_API # include "PerformanceImpl.h" #endif /* VBOX_WITH_RESOURCE_USAGE_API */ #if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT) # include #endif /* #if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT) */ #ifdef RT_OS_LINUX # include # include # include # include #endif /* RT_OS_LINUX */ #ifdef RT_OS_SOLARIS # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include /* Dynamic loading of libhal on Solaris hosts */ # ifdef VBOX_USE_LIBHAL # include "vbox-libhal.h" extern "C" char *getfullrawname(char *); # endif # include "solaris/DynLoadLibSolaris.h" /** * Solaris DVD drive list as returned by getDVDInfoFromDevTree(). */ typedef struct SOLARISDVD { struct SOLARISDVD *pNext; char szDescription[512]; char szRawDiskPath[PATH_MAX]; } SOLARISDVD; /** Pointer to a Solaris DVD descriptor. */ typedef SOLARISDVD *PSOLARISDVD; #endif /* RT_OS_SOLARIS */ #ifdef RT_OS_WINDOWS # define _WIN32_DCOM # include # include # define INITGUID # include # include # include //# include # include # include #endif /* RT_OS_WINDOWS */ #ifdef RT_OS_DARWIN # include "darwin/iokit.h" #endif #ifdef VBOX_WITH_CROGL extern bool is3DAccelerationSupported(); #endif /* VBOX_WITH_CROGL */ #include #include #include #include #include #include #include #include #ifdef RT_OS_SOLARIS # include # include #endif #ifdef VBOX_WITH_HOSTNETIF_API # include "netif.h" #endif /* XXX Solaris: definitions in /usr/include/sys/regset.h clash with hwacc_svm.h */ #undef DS #undef ES #undef CS #undef SS #undef FS #undef GS #include #include #include #include #include #include #include "VBox/com/MultiResult.h" #include #include //////////////////////////////////////////////////////////////////////////////// // // Host private data definition // //////////////////////////////////////////////////////////////////////////////// struct Host::Data { Data() : #ifdef VBOX_WITH_USB usbListsLock(LOCKCLASS_USBLIST), #endif drivesLock(LOCKCLASS_LISTOFMEDIA), fDVDDrivesListBuilt(false), fFloppyDrivesListBuilt(false) {}; VirtualBox *pParent; #ifdef VBOX_WITH_USB WriteLockHandle usbListsLock; // protects the below two lists USBDeviceFilterList llChildren; // all USB device filters USBDeviceFilterList llUSBDeviceFilters; // USB device filters in use by the USB proxy service /** Pointer to the USBProxyService object. */ USBProxyService *pUSBProxyService; #endif /* VBOX_WITH_USB */ // list of host drives; lazily created by getDVDDrives() and getFloppyDrives() WriteLockHandle drivesLock; // protects the below two lists and the bools MediaList llDVDDrives, llFloppyDrives; bool fDVDDrivesListBuilt, fFloppyDrivesListBuilt; #if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) /** Object with information about host drives */ VBoxMainDriveInfo hostDrives; #endif /* Features that can be queried with GetProcessorFeature */ BOOL fVTSupported, fLongModeSupported, fPAESupported, fNestedPagingSupported; /* 3D hardware acceleration supported? */ BOOL f3DAccelerationSupported; HostPowerService *pHostPowerService; }; //////////////////////////////////////////////////////////////////////////////// // // Constructor / destructor // //////////////////////////////////////////////////////////////////////////////// HRESULT Host::FinalConstruct() { return BaseFinalConstruct(); } void Host::FinalRelease() { uninit(); BaseFinalRelease(); } /** * Initializes the host object. * * @param aParent VirtualBox parent object. */ HRESULT Host::init(VirtualBox *aParent) { HRESULT hrc; LogFlowThisFunc(("aParent=%p\n", aParent)); /* Enclose the state transition NotReady->InInit->Ready */ AutoInitSpan autoInitSpan(this); AssertReturn(autoInitSpan.isOk(), E_FAIL); m = new Data(); m->pParent = aParent; #ifdef VBOX_WITH_USB /* * Create and initialize the USB Proxy Service. */ # if defined (RT_OS_DARWIN) m->pUSBProxyService = new USBProxyServiceDarwin(this); # elif defined (RT_OS_LINUX) m->pUSBProxyService = new USBProxyServiceLinux(this); # elif defined (RT_OS_OS2) m->pUSBProxyService = new USBProxyServiceOs2(this); # elif defined (RT_OS_SOLARIS) m->pUSBProxyService = new USBProxyServiceSolaris(this); # elif defined (RT_OS_WINDOWS) m->pUSBProxyService = new USBProxyServiceWindows(this); # elif defined (RT_OS_FREEBSD) m->pUSBProxyService = new USBProxyServiceFreeBSD(this); # else m->pUSBProxyService = new USBProxyService(this); # endif hrc = m->pUSBProxyService->init(); AssertComRCReturn(hrc, hrc); #endif /* VBOX_WITH_USB */ #ifdef VBOX_WITH_RESOURCE_USAGE_API registerMetrics(aParent->performanceCollector()); #endif /* VBOX_WITH_RESOURCE_USAGE_API */ #if defined (RT_OS_WINDOWS) m->pHostPowerService = new HostPowerServiceWin(m->pParent); #elif defined (RT_OS_DARWIN) m->pHostPowerService = new HostPowerServiceDarwin(m->pParent); #else m->pHostPowerService = new HostPowerService(m->pParent); #endif /* Cache the features reported by GetProcessorFeature. */ m->fVTSupported = false; m->fLongModeSupported = false; m->fPAESupported = false; m->fNestedPagingSupported = false; if (ASMHasCpuId()) { uint32_t u32FeaturesECX; uint32_t u32Dummy; uint32_t u32FeaturesEDX; uint32_t u32VendorEBX, u32VendorECX, u32VendorEDX, u32AMDFeatureEDX, u32AMDFeatureECX; ASMCpuId(0, &u32Dummy, &u32VendorEBX, &u32VendorECX, &u32VendorEDX); ASMCpuId(1, &u32Dummy, &u32Dummy, &u32FeaturesECX, &u32FeaturesEDX); /* Query AMD features. */ ASMCpuId(0x80000001, &u32Dummy, &u32Dummy, &u32AMDFeatureECX, &u32AMDFeatureEDX); m->fLongModeSupported = !!(u32AMDFeatureEDX & X86_CPUID_AMD_FEATURE_EDX_LONG_MODE); m->fPAESupported = !!(u32FeaturesEDX & X86_CPUID_FEATURE_EDX_PAE); if ( u32VendorEBX == X86_CPUID_VENDOR_INTEL_EBX && u32VendorECX == X86_CPUID_VENDOR_INTEL_ECX && u32VendorEDX == X86_CPUID_VENDOR_INTEL_EDX ) { if ( (u32FeaturesECX & X86_CPUID_FEATURE_ECX_VMX) && (u32FeaturesEDX & X86_CPUID_FEATURE_EDX_MSR) && (u32FeaturesEDX & X86_CPUID_FEATURE_EDX_FXSR) ) { int rc = SUPR3QueryVTxSupported(); if (RT_SUCCESS(rc)) m->fVTSupported = true; } } else if ( u32VendorEBX == X86_CPUID_VENDOR_AMD_EBX && u32VendorECX == X86_CPUID_VENDOR_AMD_ECX && u32VendorEDX == X86_CPUID_VENDOR_AMD_EDX ) { if ( (u32AMDFeatureECX & X86_CPUID_AMD_FEATURE_ECX_SVM) && (u32FeaturesEDX & X86_CPUID_FEATURE_EDX_MSR) && (u32FeaturesEDX & X86_CPUID_FEATURE_EDX_FXSR) ) { uint32_t u32SVMFeatureEDX; m->fVTSupported = true; /* Query AMD features. */ ASMCpuId(0x8000000A, &u32Dummy, &u32Dummy, &u32Dummy, &u32SVMFeatureEDX); if (u32SVMFeatureEDX & AMD_CPUID_SVM_FEATURE_EDX_NESTED_PAGING) m->fNestedPagingSupported = true; } } } #if 0 /* needs testing */ if (m->fVTSupported) { uint32_t u32Caps = 0; int rc = SUPR3QueryVTCaps(&u32Caps); if (RT_SUCCESS(rc)) { if (u32Caps & SUPVTCAPS_NESTED_PAGING) m->fNestedPagingSupported = true; } /* else @todo; report BIOS trouble in some way. */ } #endif /* Test for 3D hardware acceleration support */ m->f3DAccelerationSupported = false; #ifdef VBOX_WITH_CROGL m->f3DAccelerationSupported = is3DAccelerationSupported(); #endif /* VBOX_WITH_CROGL */ #if defined (RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) /* Extract the list of configured host-only interfaces */ std::set aConfiguredNames; SafeArray aGlobalExtraDataKeys; hrc = aParent->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys)); AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc)); for (size_t i = 0; i < aGlobalExtraDataKeys.size(); ++i) { Utf8Str strKey = aGlobalExtraDataKeys[i]; if (!strKey.startsWith("HostOnly/vboxnet")) continue; size_t pos = strKey.find("/", sizeof("HostOnly/vboxnet")); if (pos != Utf8Str::npos) aConfiguredNames.insert(strKey.substr(sizeof("HostOnly"), pos - sizeof("HostOnly"))); } for (std::set::const_iterator it = aConfiguredNames.begin(); it != aConfiguredNames.end(); ++it) { ComPtr hif; ComPtr progress; int r = NetIfCreateHostOnlyNetworkInterface(m->pParent, hif.asOutParam(), progress.asOutParam(), it->c_str()); if (RT_FAILURE(r)) return E_FAIL; } #endif /* defined (RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) */ /* Confirm a successful initialization */ autoInitSpan.setSucceeded(); return S_OK; } /** * Uninitializes the host object and sets the ready flag to FALSE. * Called either from FinalRelease() or by the parent when it gets destroyed. */ void Host::uninit() { LogFlowThisFunc(("\n")); /* Enclose the state transition Ready->InUninit->NotReady */ AutoUninitSpan autoUninitSpan(this); if (autoUninitSpan.uninitDone()) return; #ifdef VBOX_WITH_RESOURCE_USAGE_API unregisterMetrics (m->pParent->performanceCollector()); #endif /* VBOX_WITH_RESOURCE_USAGE_API */ #ifdef VBOX_WITH_USB /* wait for USB proxy service to terminate before we uninit all USB * devices */ LogFlowThisFunc(("Stopping USB proxy service...\n")); delete m->pUSBProxyService; m->pUSBProxyService = NULL; LogFlowThisFunc(("Done stopping USB proxy service.\n")); #endif delete m->pHostPowerService; #ifdef VBOX_WITH_USB /* uninit all USB device filters still referenced by clients * Note! HostUSBDeviceFilter::uninit() will modify llChildren. */ while (!m->llChildren.empty()) { ComObjPtr &pChild = m->llChildren.front(); pChild->uninit(); } m->llUSBDeviceFilters.clear(); #endif delete m; m = NULL; } //////////////////////////////////////////////////////////////////////////////// // // ISnapshot public methods // //////////////////////////////////////////////////////////////////////////////// /** * Returns a list of host DVD drives. * * @returns COM status code * @param drives address of result pointer */ STDMETHODIMP Host::COMGETTER(DVDDrives)(ComSafeArrayOut(IMedium *, aDrives)) { CheckComArgOutSafeArrayPointerValid(aDrives); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(m->drivesLock COMMA_LOCKVAL_SRC_POS); MediaList *pList; HRESULT rc = getDrives(DeviceType_DVD, true /* fRefresh */, pList); if (SUCCEEDED(rc)) { SafeIfaceArray array(*pList); array.detachTo(ComSafeArrayOutArg(aDrives)); } return rc; } /** * Returns a list of host floppy drives. * * @returns COM status code * @param drives address of result pointer */ STDMETHODIMP Host::COMGETTER(FloppyDrives)(ComSafeArrayOut(IMedium *, aDrives)) { CheckComArgOutPointerValid(aDrives); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(m->drivesLock COMMA_LOCKVAL_SRC_POS); MediaList *pList; HRESULT rc = getDrives(DeviceType_Floppy, true /* fRefresh */, pList); if (SUCCEEDED(rc)) { SafeIfaceArray collection(*pList); collection.detachTo(ComSafeArrayOutArg(aDrives)); } return rc; } #if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT) # define VBOX_APP_NAME L"VirtualBox" static int vboxNetWinAddComponent(std::list< ComObjPtr > *pPist, INetCfgComponent *pncc) { LPWSTR lpszName; GUID IfGuid; HRESULT hr; int rc = VERR_GENERAL_FAILURE; hr = pncc->GetDisplayName( &lpszName ); Assert(hr == S_OK); if (hr == S_OK) { Bstr name((CBSTR)lpszName); hr = pncc->GetInstanceGuid(&IfGuid); Assert(hr == S_OK); if (hr == S_OK) { /* create a new object and add it to the list */ ComObjPtr iface; iface.createObject(); /* remove the curly bracket at the end */ if (SUCCEEDED(iface->init (name, Guid (IfGuid), HostNetworkInterfaceType_Bridged))) { // iface->setVirtualBox(m->pParent); pPist->push_back(iface); rc = VINF_SUCCESS; } else { Assert(0); } } CoTaskMemFree(lpszName); } return rc; } #endif /* defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT) */ /** * Returns a list of host network interfaces. * * @returns COM status code * @param drives address of result pointer */ STDMETHODIMP Host::COMGETTER(NetworkInterfaces)(ComSafeArrayOut(IHostNetworkInterface*, aNetworkInterfaces)) { #if defined(RT_OS_WINDOWS) || defined(VBOX_WITH_NETFLT) /*|| defined(RT_OS_OS2)*/ if (ComSafeArrayOutIsNull(aNetworkInterfaces)) return E_POINTER; AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); std::list > list; # ifdef VBOX_WITH_HOSTNETIF_API int rc = NetIfList(list); if (rc) { Log(("Failed to get host network interface list with rc=%Rrc\n", rc)); } # else # if defined(RT_OS_DARWIN) PDARWINETHERNIC pEtherNICs = DarwinGetEthernetControllers(); while (pEtherNICs) { ComObjPtr IfObj; IfObj.createObject(); if (SUCCEEDED(IfObj->init(Bstr(pEtherNICs->szName), Guid(pEtherNICs->Uuid), HostNetworkInterfaceType_Bridged))) list.push_back(IfObj); /* next, free current */ void *pvFree = pEtherNICs; pEtherNICs = pEtherNICs->pNext; RTMemFree(pvFree); } # elif defined RT_OS_WINDOWS # ifndef VBOX_WITH_NETFLT hr = E_NOTIMPL; # else /* # if defined VBOX_WITH_NETFLT */ INetCfg *pNc; INetCfgComponent *pMpNcc; INetCfgComponent *pTcpIpNcc; LPWSTR lpszApp; HRESULT hr; IEnumNetCfgBindingPath *pEnumBp; INetCfgBindingPath *pBp; IEnumNetCfgBindingInterface *pEnumBi; INetCfgBindingInterface *pBi; /* we are using the INetCfg API for getting the list of miniports */ hr = VBoxNetCfgWinQueryINetCfg( FALSE, VBOX_APP_NAME, &pNc, &lpszApp ); Assert(hr == S_OK); if (hr == S_OK) { # ifdef VBOX_NETFLT_ONDEMAND_BIND /* for the protocol-based approach for now we just get all miniports the MS_TCPIP protocol binds to */ hr = pNc->FindComponent(L"MS_TCPIP", &pTcpIpNcc); # else /* for the filter-based approach we get all miniports our filter (sun_VBoxNetFlt)is bound to */ hr = pNc->FindComponent(L"sun_VBoxNetFlt", &pTcpIpNcc); # ifndef VBOX_WITH_HARDENING if (hr != S_OK) { /* TODO: try to install the netflt from here */ } # endif # endif if (hr == S_OK) { hr = VBoxNetCfgWinGetBindingPathEnum(pTcpIpNcc, EBP_BELOW, &pEnumBp); Assert(hr == S_OK); if ( hr == S_OK ) { hr = VBoxNetCfgWinGetFirstBindingPath(pEnumBp, &pBp); Assert(hr == S_OK || hr == S_FALSE); while( hr == S_OK ) { /* S_OK == enabled, S_FALSE == disabled */ if (pBp->IsEnabled() == S_OK) { hr = VBoxNetCfgWinGetBindingInterfaceEnum(pBp, &pEnumBi); Assert(hr == S_OK); if ( hr == S_OK ) { hr = VBoxNetCfgWinGetFirstBindingInterface(pEnumBi, &pBi); Assert(hr == S_OK); while(hr == S_OK) { hr = pBi->GetLowerComponent( &pMpNcc ); Assert(hr == S_OK); if (hr == S_OK) { ULONG uComponentStatus; hr = pMpNcc->GetDeviceStatus(&uComponentStatus); Assert(hr == S_OK); if (hr == S_OK) { if (uComponentStatus == 0) { vboxNetWinAddComponent(&list, pMpNcc); } } VBoxNetCfgWinReleaseRef( pMpNcc ); } VBoxNetCfgWinReleaseRef(pBi); hr = VBoxNetCfgWinGetNextBindingInterface(pEnumBi, &pBi); } VBoxNetCfgWinReleaseRef(pEnumBi); } } VBoxNetCfgWinReleaseRef(pBp); hr = VBoxNetCfgWinGetNextBindingPath(pEnumBp, &pBp); } VBoxNetCfgWinReleaseRef(pEnumBp); } VBoxNetCfgWinReleaseRef(pTcpIpNcc); } else { LogRel(("failed to get the sun_VBoxNetFlt component, error (0x%x)", hr)); } VBoxNetCfgWinReleaseINetCfg(pNc, FALSE); } # endif /* # if defined VBOX_WITH_NETFLT */ # elif defined RT_OS_LINUX int sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock >= 0) { char pBuffer[2048]; struct ifconf ifConf; ifConf.ifc_len = sizeof(pBuffer); ifConf.ifc_buf = pBuffer; if (ioctl(sock, SIOCGIFCONF, &ifConf) >= 0) { for (struct ifreq *pReq = ifConf.ifc_req; (char*)pReq < pBuffer + ifConf.ifc_len; pReq++) { if (ioctl(sock, SIOCGIFHWADDR, pReq) >= 0) { if (pReq->ifr_hwaddr.sa_family == ARPHRD_ETHER) { RTUUID uuid; Assert(sizeof(uuid) <= sizeof(*pReq)); memcpy(&uuid, pReq, sizeof(uuid)); ComObjPtr IfObj; IfObj.createObject(); if (SUCCEEDED(IfObj->init(Bstr(pReq->ifr_name), Guid(uuid), HostNetworkInterfaceType_Bridged))) list.push_back(IfObj); } } } } close(sock); } # endif /* RT_OS_LINUX */ # endif std::list >::iterator it; for (it = list.begin(); it != list.end(); ++it) { (*it)->setVirtualBox(m->pParent); } SafeIfaceArray networkInterfaces (list); networkInterfaces.detachTo(ComSafeArrayOutArg(aNetworkInterfaces)); return S_OK; #else /* Not implemented / supported on this platform. */ ReturnComNotImplemented(); #endif } STDMETHODIMP Host::COMGETTER(USBDevices)(ComSafeArrayOut(IHostUSBDevice*, aUSBDevices)) { #ifdef VBOX_WITH_USB CheckComArgOutSafeArrayPointerValid(aUSBDevices); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); HRESULT rc = checkUSBProxyService(); if (FAILED(rc)) return rc; return m->pUSBProxyService->getDeviceCollection(ComSafeArrayOutArg(aUSBDevices)); #else /* Note: The GUI depends on this method returning E_NOTIMPL with no * extended error info to indicate that USB is simply not available * (w/o treating it as a failure), for example, as in OSE. */ NOREF(aUSBDevices); # ifndef RT_OS_WINDOWS NOREF(aUSBDevicesSize); # endif ReturnComNotImplemented(); #endif } STDMETHODIMP Host::COMGETTER(USBDeviceFilters)(ComSafeArrayOut(IHostUSBDeviceFilter*, aUSBDeviceFilters)) { #ifdef VBOX_WITH_USB CheckComArgOutSafeArrayPointerValid(aUSBDeviceFilters); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoMultiWriteLock2 alock(this->lockHandle(), &m->usbListsLock COMMA_LOCKVAL_SRC_POS); HRESULT rc = checkUSBProxyService(); if (FAILED(rc)) return rc; SafeIfaceArray collection(m->llUSBDeviceFilters); collection.detachTo(ComSafeArrayOutArg(aUSBDeviceFilters)); return rc; #else /* Note: The GUI depends on this method returning E_NOTIMPL with no * extended error info to indicate that USB is simply not available * (w/o treating it as a failure), for example, as in OSE. */ NOREF(aUSBDeviceFilters); # ifndef RT_OS_WINDOWS NOREF(aUSBDeviceFiltersSize); # endif ReturnComNotImplemented(); #endif } /** * Returns the number of installed logical processors * * @returns COM status code * @param count address of result variable */ STDMETHODIMP Host::COMGETTER(ProcessorCount)(ULONG *aCount) { CheckComArgOutPointerValid(aCount); // no locking required *aCount = RTMpGetPresentCount(); return S_OK; } /** * Returns the number of online logical processors * * @returns COM status code * @param count address of result variable */ STDMETHODIMP Host::COMGETTER(ProcessorOnlineCount)(ULONG *aCount) { CheckComArgOutPointerValid(aCount); // no locking required *aCount = RTMpGetOnlineCount(); return S_OK; } /** * Returns the number of installed physical processor cores. * * @returns COM status code * @param count address of result variable */ STDMETHODIMP Host::COMGETTER(ProcessorCoreCount)(ULONG *aCount) { CheckComArgOutPointerValid(aCount); // no locking required return E_NOTIMPL; } /** * Returns the (approximate) maximum speed of the given host CPU in MHz * * @returns COM status code * @param cpu id to get info for. * @param speed address of result variable, speed is 0 if unknown or aCpuId is invalid. */ STDMETHODIMP Host::GetProcessorSpeed(ULONG aCpuId, ULONG *aSpeed) { CheckComArgOutPointerValid(aSpeed); // no locking required *aSpeed = RTMpGetMaxFrequency(aCpuId); return S_OK; } /** * Returns a description string for the host CPU * * @returns COM status code * @param cpu id to get info for. * @param description address of result variable, empty string if not known or aCpuId is invalid. */ STDMETHODIMP Host::GetProcessorDescription(ULONG aCpuId, BSTR *aDescription) { CheckComArgOutPointerValid(aDescription); // no locking required char szCPUModel[80]; int vrc = RTMpGetDescription(aCpuId, szCPUModel, sizeof(szCPUModel)); if (RT_FAILURE(vrc)) return E_FAIL; /** @todo error reporting? */ Bstr (szCPUModel).cloneTo(aDescription); return S_OK; } /** * Returns whether a host processor feature is supported or not * * @returns COM status code * @param Feature to query. * @param address of supported bool result variable */ STDMETHODIMP Host::GetProcessorFeature(ProcessorFeature_T aFeature, BOOL *aSupported) { CheckComArgOutPointerValid(aSupported); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); switch (aFeature) { case ProcessorFeature_HWVirtEx: *aSupported = m->fVTSupported; break; case ProcessorFeature_PAE: *aSupported = m->fPAESupported; break; case ProcessorFeature_LongMode: *aSupported = m->fLongModeSupported; break; case ProcessorFeature_NestedPaging: *aSupported = m->fNestedPagingSupported; break; default: ReturnComNotImplemented(); } return S_OK; } /** * Returns the specific CPUID leaf. * * @returns COM status code * @param aCpuId The CPU number. Mostly ignored. * @param aLeaf The leaf number. * @param aSubLeaf The sub-leaf number. * @param aValEAX Where to return EAX. * @param aValEBX Where to return EBX. * @param aValECX Where to return ECX. * @param aValEDX Where to return EDX. */ STDMETHODIMP Host::GetProcessorCPUIDLeaf(ULONG aCpuId, ULONG aLeaf, ULONG aSubLeaf, ULONG *aValEAX, ULONG *aValEBX, ULONG *aValECX, ULONG *aValEDX) { CheckComArgOutPointerValid(aValEAX); CheckComArgOutPointerValid(aValEBX); CheckComArgOutPointerValid(aValECX); CheckComArgOutPointerValid(aValEDX); // no locking required /* Check that the CPU is online. */ /** @todo later use RTMpOnSpecific. */ if (!RTMpIsCpuOnline(aCpuId)) return RTMpIsCpuPresent(aCpuId) ? setError(E_FAIL, tr("CPU no.%u is not present"), aCpuId) : setError(E_FAIL, tr("CPU no.%u is not online"), aCpuId); uint32_t uEAX, uEBX, uECX, uEDX; ASMCpuId_Idx_ECX(aLeaf, aSubLeaf, &uEAX, &uEBX, &uECX, &uEDX); *aValEAX = uEAX; *aValEBX = uEBX; *aValECX = uECX; *aValEDX = uEDX; return S_OK; } /** * Returns the amount of installed system memory in megabytes * * @returns COM status code * @param size address of result variable */ STDMETHODIMP Host::COMGETTER(MemorySize)(ULONG *aSize) { CheckComArgOutPointerValid(aSize); // no locking required /* @todo This is an ugly hack. There must be a function in IPRT for that. */ pm::CollectorHAL *hal = pm::createHAL(); if (!hal) return E_FAIL; ULONG tmp; int rc = hal->getHostMemoryUsage(aSize, &tmp, &tmp); *aSize /= 1024; delete hal; return rc; } /** * Returns the current system memory free space in megabytes * * @returns COM status code * @param available address of result variable */ STDMETHODIMP Host::COMGETTER(MemoryAvailable)(ULONG *aAvailable) { CheckComArgOutPointerValid(aAvailable); // no locking required /* @todo This is an ugly hack. There must be a function in IPRT for that. */ pm::CollectorHAL *hal = pm::createHAL(); if (!hal) return E_FAIL; ULONG tmp; int rc = hal->getHostMemoryUsage(&tmp, &tmp, aAvailable); *aAvailable /= 1024; delete hal; return rc; } /** * Returns the name string of the host operating system * * @returns COM status code * @param os address of result variable */ STDMETHODIMP Host::COMGETTER(OperatingSystem)(BSTR *aOs) { CheckComArgOutPointerValid(aOs); // no locking required char szOSName[80]; int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szOSName, sizeof(szOSName)); if (RT_FAILURE(vrc)) return E_FAIL; /** @todo error reporting? */ Bstr (szOSName).cloneTo(aOs); return S_OK; } /** * Returns the version string of the host operating system * * @returns COM status code * @param os address of result variable */ STDMETHODIMP Host::COMGETTER(OSVersion)(BSTR *aVersion) { CheckComArgOutPointerValid(aVersion); // no locking required /* Get the OS release. Reserve some buffer space for the service pack. */ char szOSRelease[128]; int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOSRelease, sizeof(szOSRelease) - 32); if (RT_FAILURE(vrc)) return E_FAIL; /** @todo error reporting? */ /* Append the service pack if present. */ char szOSServicePack[80]; vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szOSServicePack, sizeof(szOSServicePack)); if (RT_FAILURE(vrc)) { if (vrc != VERR_NOT_SUPPORTED) return E_FAIL; /** @todo error reporting? */ szOSServicePack[0] = '\0'; } if (szOSServicePack[0] != '\0') { char *psz = strchr(szOSRelease, '\0'); RTStrPrintf(psz, &szOSRelease[sizeof(szOSRelease)] - psz, "sp%s", szOSServicePack); } Bstr(szOSRelease).cloneTo(aVersion); return S_OK; } /** * Returns the current host time in milliseconds since 1970-01-01 UTC. * * @returns COM status code * @param time address of result variable */ STDMETHODIMP Host::COMGETTER(UTCTime)(LONG64 *aUTCTime) { CheckComArgOutPointerValid(aUTCTime); // no locking required RTTIMESPEC now; *aUTCTime = RTTimeSpecGetMilli(RTTimeNow(&now)); return S_OK; } STDMETHODIMP Host::COMGETTER(Acceleration3DAvailable)(BOOL *aSupported) { CheckComArgOutPointerValid(aSupported); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); *aSupported = m->f3DAccelerationSupported; return S_OK; } STDMETHODIMP Host::CreateHostOnlyNetworkInterface(IHostNetworkInterface **aHostNetworkInterface, IProgress **aProgress) { CheckComArgOutPointerValid(aHostNetworkInterface); CheckComArgOutPointerValid(aProgress); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); #ifdef VBOX_WITH_HOSTNETIF_API /* No need to lock anything. If there ever will - watch out, the function * called below grabs the VirtualBox lock. */ int r = NetIfCreateHostOnlyNetworkInterface(m->pParent, aHostNetworkInterface, aProgress); if (RT_SUCCESS(r)) { #if !defined(RT_OS_WINDOWS) Bstr tmpAddr, tmpMask, tmpName; HRESULT hrc; hrc = (*aHostNetworkInterface)->COMGETTER(Name)(tmpName.asOutParam()); ComAssertComRCRet(hrc, hrc); hrc = (*aHostNetworkInterface)->COMGETTER(IPAddress)(tmpAddr.asOutParam()); ComAssertComRCRet(hrc, hrc); hrc = (*aHostNetworkInterface)->COMGETTER(NetworkMask)(tmpMask.asOutParam()); ComAssertComRCRet(hrc, hrc); /* * We need to write the default IP address and mask to extra data now, * so the interface gets re-created after vboxnetadp.ko reload. * Note that we avoid calling EnableStaticIpConfig since it would * change the address on host's interface as well and we want to * postpone the change until VM actually starts. */ hrc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPAddress", tmpName.raw()).raw(), tmpAddr.raw()); ComAssertComRCRet(hrc, hrc); hrc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPNetMask", tmpName.raw()).raw(), tmpMask.raw()); ComAssertComRCRet(hrc, hrc); #endif return S_OK; } return r == VERR_NOT_IMPLEMENTED ? E_NOTIMPL : E_FAIL; #else return E_NOTIMPL; #endif } STDMETHODIMP Host::RemoveHostOnlyNetworkInterface(IN_BSTR aId, IProgress **aProgress) { CheckComArgOutPointerValid(aProgress); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); #ifdef VBOX_WITH_HOSTNETIF_API /* No need to lock anything, the code below does not touch the state * of the host object. If that ever changes then check for lock order * violations with the called functions. */ Bstr name; HRESULT rc; /* first check whether an interface with the given name already exists */ { ComPtr iface; if (FAILED(FindHostNetworkInterfaceById(aId, iface.asOutParam()))) return setError(VBOX_E_OBJECT_NOT_FOUND, tr("Host network interface with UUID {%RTuuid} does not exist"), Guid (aId).raw()); rc = iface->COMGETTER(Name)(name.asOutParam()); ComAssertComRCRet(rc, rc); } int r = NetIfRemoveHostOnlyNetworkInterface(m->pParent, Guid(aId).ref(), aProgress); if (RT_SUCCESS(r)) { /* Drop configuration parameters for removed interface */ rc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPAddress", name.raw()).raw(), NULL); rc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPNetMask", name.raw()).raw(), NULL); rc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPV6Address", name.raw()).raw(), NULL); rc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPV6NetMask", name.raw()).raw(), NULL); return S_OK; } return r == VERR_NOT_IMPLEMENTED ? E_NOTIMPL : E_FAIL; #else return E_NOTIMPL; #endif } STDMETHODIMP Host::CreateUSBDeviceFilter(IN_BSTR aName, IHostUSBDeviceFilter **aFilter) { #ifdef VBOX_WITH_USB CheckComArgStrNotEmptyOrNull(aName); CheckComArgOutPointerValid(aFilter); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); ComObjPtr filter; filter.createObject(); HRESULT rc = filter->init(this, aName); ComAssertComRCRet(rc, rc); rc = filter.queryInterfaceTo(aFilter); AssertComRCReturn(rc, rc); return S_OK; #else /* Note: The GUI depends on this method returning E_NOTIMPL with no * extended error info to indicate that USB is simply not available * (w/o treating it as a failure), for example, as in OSE. */ NOREF(aName); NOREF(aFilter); ReturnComNotImplemented(); #endif } STDMETHODIMP Host::InsertUSBDeviceFilter(ULONG aPosition, IHostUSBDeviceFilter *aFilter) { #ifdef VBOX_WITH_USB CheckComArgNotNull(aFilter); /* Note: HostUSBDeviceFilter and USBProxyService also uses this lock. */ AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoMultiWriteLock2 alock(this->lockHandle(), &m->usbListsLock COMMA_LOCKVAL_SRC_POS); clearError(); MultiResult rc = checkUSBProxyService(); if (FAILED(rc)) return rc; ComObjPtr pFilter; for (USBDeviceFilterList::iterator it = m->llChildren.begin(); it != m->llChildren.end(); ++it) { if (*it == aFilter) { pFilter = *it; break; } } if (pFilter.isNull()) return setError(VBOX_E_INVALID_OBJECT_STATE, tr("The given USB device filter is not created within this VirtualBox instance")); if (pFilter->mInList) return setError(E_INVALIDARG, tr("The given USB device filter is already in the list")); /* iterate to the position... */ USBDeviceFilterList::iterator itPos = m->llUSBDeviceFilters.begin(); std::advance(itPos, aPosition); /* ...and insert */ m->llUSBDeviceFilters.insert(itPos, pFilter); pFilter->mInList = true; /* notify the proxy (only when the filter is active) */ if ( m->pUSBProxyService->isActive() && pFilter->getData().mActive) { ComAssertRet(pFilter->getId() == NULL, E_FAIL); pFilter->getId() = m->pUSBProxyService->insertFilter(&pFilter->getData().mUSBFilter); } // save the global settings; for that we should hold only the VirtualBox lock alock.release(); AutoWriteLock vboxLock(m->pParent COMMA_LOCKVAL_SRC_POS); return rc = m->pParent->saveSettings(); #else /* Note: The GUI depends on this method returning E_NOTIMPL with no * extended error info to indicate that USB is simply not available * (w/o treating it as a failure), for example, as in OSE. */ NOREF(aPosition); NOREF(aFilter); ReturnComNotImplemented(); #endif } STDMETHODIMP Host::RemoveUSBDeviceFilter(ULONG aPosition) { #ifdef VBOX_WITH_USB /* Note: HostUSBDeviceFilter and USBProxyService also uses this lock. */ AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoMultiWriteLock2 alock(this->lockHandle(), &m->usbListsLock COMMA_LOCKVAL_SRC_POS); clearError(); MultiResult rc = checkUSBProxyService(); if (FAILED(rc)) return rc; if (!m->llUSBDeviceFilters.size()) return setError(E_INVALIDARG, tr("The USB device filter list is empty")); if (aPosition >= m->llUSBDeviceFilters.size()) return setError(E_INVALIDARG, tr("Invalid position: %lu (must be in range [0, %lu])"), aPosition, m->llUSBDeviceFilters.size() - 1); ComObjPtr filter; { /* iterate to the position... */ USBDeviceFilterList::iterator it = m->llUSBDeviceFilters.begin(); std::advance (it, aPosition); /* ...get an element from there... */ filter = *it; /* ...and remove */ filter->mInList = false; m->llUSBDeviceFilters.erase(it); } /* notify the proxy (only when the filter is active) */ if (m->pUSBProxyService->isActive() && filter->getData().mActive) { ComAssertRet(filter->getId() != NULL, E_FAIL); m->pUSBProxyService->removeFilter(filter->getId()); filter->getId() = NULL; } // save the global settings; for that we should hold only the VirtualBox lock alock.release(); AutoWriteLock vboxLock(m->pParent COMMA_LOCKVAL_SRC_POS); return rc = m->pParent->saveSettings(); #else /* Note: The GUI depends on this method returning E_NOTIMPL with no * extended error info to indicate that USB is simply not available * (w/o treating it as a failure), for example, as in OSE. */ NOREF(aPosition); ReturnComNotImplemented(); #endif } STDMETHODIMP Host::FindHostDVDDrive(IN_BSTR aName, IMedium **aDrive) { CheckComArgStrNotEmptyOrNull(aName); CheckComArgOutPointerValid(aDrive); ComObjPtrmedium; HRESULT rc = findHostDriveByNameOrId(DeviceType_DVD, Utf8Str(aName), medium); if (SUCCEEDED(rc)) return medium.queryInterfaceTo(aDrive); else return setError(rc, Medium::tr("The host DVD drive named '%ls' could not be found"), aName); } STDMETHODIMP Host::FindHostFloppyDrive(IN_BSTR aName, IMedium **aDrive) { CheckComArgStrNotEmptyOrNull(aName); CheckComArgOutPointerValid(aDrive); *aDrive = NULL; ComObjPtrmedium; HRESULT rc = findHostDriveByNameOrId(DeviceType_Floppy, Utf8Str(aName), medium); if (SUCCEEDED(rc)) return medium.queryInterfaceTo(aDrive); else return setError(rc, Medium::tr("The host floppy drive named '%ls' could not be found"), aName); } STDMETHODIMP Host::FindHostNetworkInterfaceByName(IN_BSTR name, IHostNetworkInterface **networkInterface) { #ifndef VBOX_WITH_HOSTNETIF_API return E_NOTIMPL; #else if (!name) return E_INVALIDARG; if (!networkInterface) return E_POINTER; *networkInterface = NULL; ComObjPtr found; std::list > list; int rc = NetIfList(list); if (RT_FAILURE(rc)) { Log(("Failed to get host network interface list with rc=%Rrc\n", rc)); return E_FAIL; } std::list >::iterator it; for (it = list.begin(); it != list.end(); ++it) { Bstr n; (*it)->COMGETTER(Name) (n.asOutParam()); if (n == name) found = *it; } if (!found) return setError(E_INVALIDARG, HostNetworkInterface::tr("The host network interface with the given name could not be found")); found->setVirtualBox(m->pParent); return found.queryInterfaceTo(networkInterface); #endif } STDMETHODIMP Host::FindHostNetworkInterfaceById(IN_BSTR id, IHostNetworkInterface **networkInterface) { #ifndef VBOX_WITH_HOSTNETIF_API return E_NOTIMPL; #else if (Guid(id).isEmpty()) return E_INVALIDARG; if (!networkInterface) return E_POINTER; *networkInterface = NULL; ComObjPtr found; std::list > list; int rc = NetIfList(list); if (RT_FAILURE(rc)) { Log(("Failed to get host network interface list with rc=%Rrc\n", rc)); return E_FAIL; } std::list >::iterator it; for (it = list.begin(); it != list.end(); ++it) { Bstr g; (*it)->COMGETTER(Id) (g.asOutParam()); if (g == id) found = *it; } if (!found) return setError(E_INVALIDARG, HostNetworkInterface::tr("The host network interface with the given GUID could not be found")); found->setVirtualBox(m->pParent); return found.queryInterfaceTo(networkInterface); #endif } STDMETHODIMP Host::FindHostNetworkInterfacesOfType(HostNetworkInterfaceType_T type, ComSafeArrayOut(IHostNetworkInterface *, aNetworkInterfaces)) { #ifdef VBOX_WITH_HOSTNETIF_API std::list > allList; int rc = NetIfList(allList); if (RT_FAILURE(rc)) return E_FAIL; std::list > resultList; std::list >::iterator it; for (it = allList.begin(); it != allList.end(); ++it) { HostNetworkInterfaceType_T t; HRESULT hr = (*it)->COMGETTER(InterfaceType)(&t); if (FAILED(hr)) return hr; if (t == type) { (*it)->setVirtualBox(m->pParent); resultList.push_back (*it); } } SafeIfaceArray filteredNetworkInterfaces (resultList); filteredNetworkInterfaces.detachTo(ComSafeArrayOutArg(aNetworkInterfaces)); return S_OK; #else return E_NOTIMPL; #endif } STDMETHODIMP Host::FindUSBDeviceByAddress(IN_BSTR aAddress, IHostUSBDevice **aDevice) { #ifdef VBOX_WITH_USB CheckComArgStrNotEmptyOrNull(aAddress); CheckComArgOutPointerValid(aDevice); *aDevice = NULL; SafeIfaceArray devsvec; HRESULT rc = COMGETTER(USBDevices) (ComSafeArrayAsOutParam(devsvec)); if (FAILED(rc)) return rc; for (size_t i = 0; i < devsvec.size(); ++i) { Bstr address; rc = devsvec[i]->COMGETTER(Address) (address.asOutParam()); if (FAILED(rc)) return rc; if (address == aAddress) { return ComObjPtr (devsvec[i]).queryInterfaceTo(aDevice); } } return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND, tr("Could not find a USB device with address '%ls'"), aAddress); #else /* !VBOX_WITH_USB */ NOREF(aAddress); NOREF(aDevice); return E_NOTIMPL; #endif /* !VBOX_WITH_USB */ } STDMETHODIMP Host::FindUSBDeviceById(IN_BSTR aId, IHostUSBDevice **aDevice) { #ifdef VBOX_WITH_USB CheckComArgExpr(aId, Guid (aId).isEmpty() == false); CheckComArgOutPointerValid(aDevice); *aDevice = NULL; SafeIfaceArray devsvec; HRESULT rc = COMGETTER(USBDevices) (ComSafeArrayAsOutParam(devsvec)); if (FAILED(rc)) return rc; for (size_t i = 0; i < devsvec.size(); ++i) { Bstr id; rc = devsvec[i]->COMGETTER(Id) (id.asOutParam()); if (FAILED(rc)) return rc; if (id == aId) { return ComObjPtr (devsvec[i]).queryInterfaceTo(aDevice); } } return setErrorNoLog (VBOX_E_OBJECT_NOT_FOUND, tr ( "Could not find a USB device with uuid {%RTuuid}"), Guid (aId).raw()); #else /* !VBOX_WITH_USB */ NOREF(aId); NOREF(aDevice); return E_NOTIMPL; #endif /* !VBOX_WITH_USB */ } STDMETHODIMP Host::GenerateMACAddress(BSTR *aAddress) { CheckComArgOutPointerValid(aAddress); // no locking required Utf8Str mac; generateMACAddress(mac); Bstr(mac).cloneTo(aAddress); return S_OK; } // public methods only for internal purposes //////////////////////////////////////////////////////////////////////////////// HRESULT Host::loadSettings(const settings::Host &data) { HRESULT rc = S_OK; #ifdef VBOX_WITH_USB AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoMultiWriteLock2 alock(this->lockHandle(), &m->usbListsLock COMMA_LOCKVAL_SRC_POS); for (settings::USBDeviceFiltersList::const_iterator it = data.llUSBDeviceFilters.begin(); it != data.llUSBDeviceFilters.end(); ++it) { const settings::USBDeviceFilter &f = *it; ComObjPtr pFilter; pFilter.createObject(); rc = pFilter->init(this, f); if (FAILED(rc)) break; m->llUSBDeviceFilters.push_back(pFilter); pFilter->mInList = true; /* notify the proxy (only when the filter is active) */ if (pFilter->getData().mActive) { HostUSBDeviceFilter *flt = pFilter; /* resolve ambiguity */ flt->getId() = m->pUSBProxyService->insertFilter(&pFilter->getData().mUSBFilter); } } #else NOREF(data); #endif /* VBOX_WITH_USB */ return rc; } HRESULT Host::saveSettings(settings::Host &data) { #ifdef VBOX_WITH_USB AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock1(this COMMA_LOCKVAL_SRC_POS); AutoReadLock alock2(&m->usbListsLock COMMA_LOCKVAL_SRC_POS); data.llUSBDeviceFilters.clear(); for (USBDeviceFilterList::const_iterator it = m->llUSBDeviceFilters.begin(); it != m->llUSBDeviceFilters.end(); ++it) { ComObjPtr pFilter = *it; settings::USBDeviceFilter f; pFilter->saveSettings(f); data.llUSBDeviceFilters.push_back(f); } #else NOREF(data); #endif /* VBOX_WITH_USB */ return S_OK; } /** * Sets the given pointer to point to the static list of DVD or floppy * drives in the Host instance data, depending on the @a mediumType * parameter. * * This builds the list on the first call; it adds or removes host drives * that may have changed if fRefresh == true. * * The caller must hold the m->drivesLock write lock before calling this. * To protect the list to which the caller's pointer points, the caller * must also hold that lock. * * @param mediumType Must be DeviceType_Floppy or DeviceType_DVD. * @param fRefresh Whether to refresh the host drives list even if this is not the first call. * @param pll Caller's pointer which gets set to the static list of host drives. * @return */ HRESULT Host::getDrives(DeviceType_T mediumType, bool fRefresh, MediaList *&pll) { HRESULT rc = S_OK; Assert(m->drivesLock.isWriteLockOnCurrentThread()); MediaList llNew; MediaList *pllCached; bool *pfListBuilt = NULL; switch (mediumType) { case DeviceType_DVD: if (!m->fDVDDrivesListBuilt || fRefresh) { rc = buildDVDDrivesList(llNew); if (FAILED(rc)) return rc; pfListBuilt = &m->fDVDDrivesListBuilt; } pllCached = &m->llDVDDrives; break; case DeviceType_Floppy: if (!m->fFloppyDrivesListBuilt || fRefresh) { rc = buildFloppyDrivesList(llNew); if (FAILED(rc)) return rc; pfListBuilt = &m->fFloppyDrivesListBuilt; } pllCached = &m->llFloppyDrives; break; default: return E_INVALIDARG; } if (pfListBuilt) { // a list was built in llNew above: if (!*pfListBuilt) { // this was the first call (instance bool is still false): then just copy the whole list and return *pllCached = llNew; // and mark the instance data as "built" *pfListBuilt = true; } else { // list was built, and this was a subsequent call: then compare the old and the new lists // remove drives from the cached list which are no longer present for (MediaList::iterator itCached = pllCached->begin(); itCached != pllCached->end(); /*nothing */) { Medium *pCached = *itCached; const Utf8Str strLocationCached = pCached->getLocationFull(); bool fFound = false; for (MediaList::iterator itNew = llNew.begin(); itNew != llNew.end(); ++itNew) { Medium *pNew = *itNew; const Utf8Str strLocationNew = pNew->getLocationFull(); if (strLocationNew == strLocationCached) { fFound = true; break; } } if (!fFound) itCached = pllCached->erase(itCached); else ++itCached; } // add drives to the cached list that are not on there yet for (MediaList::iterator itNew = llNew.begin(); itNew != llNew.end(); ++itNew) { Medium *pNew = *itNew; const Utf8Str strLocationNew = pNew->getLocationFull(); bool fFound = false; for (MediaList::iterator itCached = pllCached->begin(); itCached != pllCached->end(); ++itCached) { Medium *pCached = *itCached; const Utf8Str strLocationCached = pCached->getLocationFull(); if (strLocationNew == strLocationCached) { fFound = true; break; } } if (!fFound) pllCached->push_back(pNew); } } } // return cached list to caller pll = pllCached; return rc; } /** * Goes through the list of host drives that would be returned by getDrives() * and looks for a host drive with the given UUID. If found, it sets pMedium * to that drive; otherwise returns VBOX_E_OBJECT_NOT_FOUND. * * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy. * @param uuid Medium UUID of host drive to look for. * @param fRefresh Whether to refresh the host drives list (see getDrives()) * @param pMedium Medium object, if found… * @return VBOX_E_OBJECT_NOT_FOUND if not found, or S_OK if found, or errors from getDrives(). */ HRESULT Host::findHostDriveById(DeviceType_T mediumType, const Guid &uuid, bool fRefresh, ComObjPtr &pMedium) { MediaList *pllMedia; AutoWriteLock wlock(m->drivesLock COMMA_LOCKVAL_SRC_POS); HRESULT rc = getDrives(mediumType, fRefresh, pllMedia); if (SUCCEEDED(rc)) { for (MediaList::iterator it = pllMedia->begin(); it != pllMedia->end(); ++it) { Medium *pThis = *it; AutoCaller mediumCaller(pThis); AutoReadLock mediumLock(pThis COMMA_LOCKVAL_SRC_POS); if (pThis->getId() == uuid) { pMedium = pThis; return S_OK; } } } return VBOX_E_OBJECT_NOT_FOUND; } /** * Goes through the list of host drives that would be returned by getDrives() * and looks for a host drive with the given name. If found, it sets pMedium * to that drive; otherwise returns VBOX_E_OBJECT_NOT_FOUND. * * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy. * @param strLocationFull Name (path) of host drive to look for. * @param fRefresh Whether to refresh the host drives list (see getDrives()) * @param pMedium Medium object, if found… * @return VBOX_E_OBJECT_NOT_FOUND if not found, or S_OK if found, or errors from getDrives(). */ HRESULT Host::findHostDriveByName(DeviceType_T mediumType, const Utf8Str &strLocationFull, bool fRefresh, ComObjPtr &pMedium) { MediaList *pllMedia; AutoWriteLock wlock(m->drivesLock COMMA_LOCKVAL_SRC_POS); HRESULT rc = getDrives(mediumType, fRefresh, pllMedia); if (SUCCEEDED(rc)) { for (MediaList::iterator it = pllMedia->begin(); it != pllMedia->end(); ++it) { Medium *pThis = *it; AutoCaller mediumCaller(pThis); AutoReadLock mediumLock(pThis COMMA_LOCKVAL_SRC_POS); if (pThis->getLocationFull() == strLocationFull) { pMedium = pThis; return S_OK; } } } return VBOX_E_OBJECT_NOT_FOUND; } /** * Goes through the list of host drives that would be returned by getDrives() * and looks for a host drive with the given name, location or ID. If found, * it sets pMedium to that drive; otherwise returns VBOX_E_OBJECT_NOT_FOUND. * * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy. * @param strNameOrId Name or full location or UUID of host drive to look for. * @param pMedium Medium object, if found… * @return VBOX_E_OBJECT_NOT_FOUND if not found, or S_OK if found, or errors from getDrives(). */ HRESULT Host::findHostDriveByNameOrId(DeviceType_T mediumType, const Utf8Str &strNameOrId, ComObjPtr &pMedium) { AutoWriteLock wlock(m->drivesLock COMMA_LOCKVAL_SRC_POS); Guid uuid(strNameOrId); if (!uuid.isEmpty()) return findHostDriveById(mediumType, uuid, true /* fRefresh */, pMedium); // string is not a syntactically valid UUID: try a name then return findHostDriveByName(mediumType, strNameOrId, true /* fRefresh */, pMedium); } /** * Called from getDrives() to build the DVD drives list. * @param pll * @return */ HRESULT Host::buildDVDDrivesList(MediaList &list) { HRESULT rc = S_OK; Assert(m->drivesLock.isWriteLockOnCurrentThread()); try { #if defined(RT_OS_WINDOWS) int sz = GetLogicalDriveStrings(0, NULL); TCHAR *hostDrives = new TCHAR[sz+1]; GetLogicalDriveStrings(sz, hostDrives); wchar_t driveName[3] = { '?', ':', '\0' }; TCHAR *p = hostDrives; do { if (GetDriveType(p) == DRIVE_CDROM) { driveName[0] = *p; ComObjPtr hostDVDDriveObj; hostDVDDriveObj.createObject(); hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(driveName)); list.push_back(hostDVDDriveObj); } p += _tcslen(p) + 1; } while (*p); delete[] hostDrives; #elif defined(RT_OS_SOLARIS) # ifdef VBOX_USE_LIBHAL if (!getDVDInfoFromHal(list)) # endif { getDVDInfoFromDevTree(list); } #elif defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) if (RT_SUCCESS(m->hostDrives.updateDVDs())) for (DriveInfoList::const_iterator it = m->hostDrives.DVDBegin(); SUCCEEDED(rc) && it != m->hostDrives.DVDEnd(); ++it) { ComObjPtr hostDVDDriveObj; Utf8Str location(it->mDevice); Utf8Str description(it->mDescription); if (SUCCEEDED(rc)) rc = hostDVDDriveObj.createObject(); if (SUCCEEDED(rc)) rc = hostDVDDriveObj->init(m->pParent, DeviceType_DVD, location, description); if (SUCCEEDED(rc)) list.push_back(hostDVDDriveObj); } #elif defined(RT_OS_DARWIN) PDARWINDVD cur = DarwinGetDVDDrives(); while (cur) { ComObjPtr hostDVDDriveObj; hostDVDDriveObj.createObject(); hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(cur->szName)); list.push_back(hostDVDDriveObj); /* next */ void *freeMe = cur; cur = cur->pNext; RTMemFree(freeMe); } #else /* PORTME */ #endif } catch(std::bad_alloc &) { rc = E_OUTOFMEMORY; } return rc; } /** * Called from getDrives() to build the floppy drives list. * @param list * @return */ HRESULT Host::buildFloppyDrivesList(MediaList &list) { HRESULT rc = S_OK; Assert(m->drivesLock.isWriteLockOnCurrentThread()); try { #ifdef RT_OS_WINDOWS int sz = GetLogicalDriveStrings(0, NULL); TCHAR *hostDrives = new TCHAR[sz+1]; GetLogicalDriveStrings(sz, hostDrives); wchar_t driveName[3] = { '?', ':', '\0' }; TCHAR *p = hostDrives; do { if (GetDriveType(p) == DRIVE_REMOVABLE) { driveName[0] = *p; ComObjPtr hostFloppyDriveObj; hostFloppyDriveObj.createObject(); hostFloppyDriveObj->init(m->pParent, DeviceType_Floppy, Bstr(driveName)); list.push_back(hostFloppyDriveObj); } p += _tcslen(p) + 1; } while (*p); delete[] hostDrives; #elif defined(RT_OS_LINUX) if (RT_SUCCESS(m->hostDrives.updateFloppies())) for (DriveInfoList::const_iterator it = m->hostDrives.FloppyBegin(); SUCCEEDED(rc) && it != m->hostDrives.FloppyEnd(); ++it) { ComObjPtr hostFloppyDriveObj; Utf8Str location(it->mDevice); Utf8Str description(it->mDescription); if (SUCCEEDED(rc)) rc = hostFloppyDriveObj.createObject(); if (SUCCEEDED(rc)) rc = hostFloppyDriveObj->init(m->pParent, DeviceType_Floppy, location, description); if (SUCCEEDED(rc)) list.push_back(hostFloppyDriveObj); } #else NOREF(list); /* PORTME */ #endif } catch(std::bad_alloc &) { rc = E_OUTOFMEMORY; } return rc; } #ifdef VBOX_WITH_USB USBProxyService* Host::usbProxyService() { return m->pUSBProxyService; } HRESULT Host::addChild(HostUSBDeviceFilter *pChild) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(&m->usbListsLock COMMA_LOCKVAL_SRC_POS); m->llChildren.push_back(pChild); return S_OK; } HRESULT Host::removeChild(HostUSBDeviceFilter *pChild) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(&m->usbListsLock COMMA_LOCKVAL_SRC_POS); for (USBDeviceFilterList::iterator it = m->llChildren.begin(); it != m->llChildren.end(); ++it) { if (*it == pChild) { m->llChildren.erase(it); break; } } return S_OK; } VirtualBox* Host::parent() { return m->pParent; } /** * Called by setter methods of all USB device filters. */ HRESULT Host::onUSBDeviceFilterChange(HostUSBDeviceFilter *aFilter, BOOL aActiveChanged /* = FALSE */) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); if (aFilter->mInList) { if (aActiveChanged) { // insert/remove the filter from the proxy if (aFilter->getData().mActive) { ComAssertRet(aFilter->getId() == NULL, E_FAIL); aFilter->getId() = m->pUSBProxyService->insertFilter(&aFilter->getData().mUSBFilter); } else { ComAssertRet(aFilter->getId() != NULL, E_FAIL); m->pUSBProxyService->removeFilter(aFilter->getId()); aFilter->getId() = NULL; } } else { if (aFilter->getData().mActive) { // update the filter in the proxy ComAssertRet(aFilter->getId() != NULL, E_FAIL); m->pUSBProxyService->removeFilter(aFilter->getId()); aFilter->getId() = m->pUSBProxyService->insertFilter(&aFilter->getData().mUSBFilter); } } // save the global settings... yeah, on every single filter property change // for that we should hold only the VirtualBox lock alock.release(); AutoWriteLock vboxLock(m->pParent COMMA_LOCKVAL_SRC_POS); return m->pParent->saveSettings(); } return S_OK; } /** * Interface for obtaining a copy of the USBDeviceFilterList, * used by the USBProxyService. * * @param aGlobalFilters Where to put the global filter list copy. * @param aMachines Where to put the machine vector. */ void Host::getUSBFilters(Host::USBDeviceFilterList *aGlobalFilters) { AutoReadLock alock(&m->usbListsLock COMMA_LOCKVAL_SRC_POS); *aGlobalFilters = m->llUSBDeviceFilters; } #endif /* VBOX_WITH_USB */ // private methods //////////////////////////////////////////////////////////////////////////////// #if defined(RT_OS_SOLARIS) && defined(VBOX_USE_LIBHAL) /** * Helper function to get the slice number from a device path * * @param pszDevLinkPath Pointer to a device path (/dev/(r)dsk/c7d1t0d0s3 etc.) * @returns Pointer to the slice portion of the given path. */ static char *solarisGetSliceFromPath(const char *pszDevLinkPath) { char *pszFound = NULL; char *pszSlice = strrchr(pszDevLinkPath, 's'); char *pszDisk = strrchr(pszDevLinkPath, 'd'); if (pszSlice && pszSlice > pszDisk) pszFound = pszSlice; else pszFound = pszDisk; if (pszFound && RT_C_IS_DIGIT(pszFound[1])) return pszFound; return NULL; } /** * Walk device links and returns an allocated path for the first one in the snapshot. * * @param DevLink Handle to the device link being walked. * @param pvArg Opaque data containing the pointer to the path. * @returns Pointer to an allocated device path string. */ static int solarisWalkDevLink(di_devlink_t DevLink, void *pvArg) { char **ppszPath = (char **)pvArg; *ppszPath = strdup(di_devlink_path(DevLink)); return DI_WALK_TERMINATE; } /** * Walk all devices in the system and enumerate CD/DVD drives. * @param Node Handle to the current node. * @param pvArg Opaque data (holds list pointer). * @returns Solaris specific code whether to continue walking or not. */ static int solarisWalkDeviceNodeForDVD(di_node_t Node, void *pvArg) { PSOLARISDVD *ppDrives = (PSOLARISDVD *)pvArg; /* * Check for "removable-media" or "hotpluggable" instead of "SCSI" so that we also include USB CD-ROMs. * As unfortunately the Solaris drivers only export these common properties. */ int *pInt = NULL; if ( di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "removable-media", &pInt) >= 0 || di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "hotpluggable", &pInt) >= 0) { if (di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "inquiry-device-type", &pInt) > 0 && ( *pInt == DTYPE_RODIRECT /* CDROM */ || *pInt == DTYPE_OPTICAL)) /* Optical Drive */ { char *pszProduct = NULL; if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "inquiry-product-id", &pszProduct) > 0) { char *pszVendor = NULL; if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "inquiry-vendor-id", &pszVendor) > 0) { /* * Found a DVD drive, we need to scan the minor nodes to find the correct * slice that represents the whole drive. "s2" is always the whole drive for CD/DVDs. */ int Major = di_driver_major(Node); di_minor_t Minor = DI_MINOR_NIL; di_devlink_handle_t DevLink = di_devlink_init(NULL /* name */, 0 /* flags */); if (DevLink) { while ((Minor = di_minor_next(Node, Minor)) != DI_MINOR_NIL) { dev_t Dev = di_minor_devt(Minor); if ( Major != (int)major(Dev) || di_minor_spectype(Minor) == S_IFBLK || di_minor_type(Minor) != DDM_MINOR) { continue; } char *pszMinorPath = di_devfs_minor_path(Minor); if (!pszMinorPath) continue; char *pszDevLinkPath = NULL; di_devlink_walk(DevLink, NULL, pszMinorPath, DI_PRIMARY_LINK, &pszDevLinkPath, solarisWalkDevLink); di_devfs_path_free(pszMinorPath); if (pszDevLinkPath) { char *pszSlice = solarisGetSliceFromPath(pszDevLinkPath); if ( pszSlice && !strcmp(pszSlice, "s2") && !strncmp(pszDevLinkPath, "/dev/rdsk", sizeof("/dev/rdsk") - 1)) /* We want only raw disks */ { /* * We've got a fully qualified DVD drive. Add it to the list. */ PSOLARISDVD pDrive = (PSOLARISDVD)RTMemAllocZ(sizeof(SOLARISDVD)); if (RT_LIKELY(pDrive)) { RTStrPrintf(pDrive->szDescription, sizeof(pDrive->szDescription), "%s %s", pszVendor, pszProduct); RTStrCopy(pDrive->szRawDiskPath, sizeof(pDrive->szRawDiskPath), pszDevLinkPath); if (*ppDrives) pDrive->pNext = *ppDrives; *ppDrives = pDrive; /* We're not interested in any of the other slices, stop minor nodes traversal. */ free(pszDevLinkPath); break; } } free(pszDevLinkPath); } } di_devlink_fini(&DevLink); } } } } } return DI_WALK_CONTINUE; } /** * Solaris specific function to enumerate CD/DVD drives via the device tree. * Works on Solaris 10 as well as OpenSolaris without depending on libhal. */ void Host::getDVDInfoFromDevTree(std::list > &list) { PSOLARISDVD pDrives = NULL; di_node_t RootNode = di_init("/", DINFOCPYALL); if (RootNode != DI_NODE_NIL) di_walk_node(RootNode, DI_WALK_CLDFIRST, &pDrives, solarisWalkDeviceNodeForDVD); di_fini(RootNode); while (pDrives) { ComObjPtr hostDVDDriveObj; hostDVDDriveObj.createObject(); hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(pDrives->szRawDiskPath), Bstr(pDrives->szDescription)); list.push_back(hostDVDDriveObj); void *pvDrive = pDrives; pDrives = pDrives->pNext; RTMemFree(pvDrive); } } /* Solaris hosts, loading libhal at runtime */ /** * Helper function to query the hal subsystem for information about DVD drives attached to the * system. * * @returns true if information was successfully obtained, false otherwise * @retval list drives found will be attached to this list */ bool Host::getDVDInfoFromHal(std::list > &list) { bool halSuccess = false; DBusError dbusError; if (!gLibHalCheckPresence()) return false; gDBusErrorInit (&dbusError); DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError); if (dbusConnection != 0) { LibHalContext *halContext = gLibHalCtxNew(); if (halContext != 0) { if (gLibHalCtxSetDBusConnection (halContext, dbusConnection)) { if (gLibHalCtxInit(halContext, &dbusError)) { int numDevices; char **halDevices = gLibHalFindDeviceStringMatch(halContext, "storage.drive_type", "cdrom", &numDevices, &dbusError); if (halDevices != 0) { /* Hal is installed and working, so if no devices are reported, assume that there are none. */ halSuccess = true; for (int i = 0; i < numDevices; i++) { char *devNode = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "block.device", &dbusError); #ifdef RT_OS_SOLARIS /* The CD/DVD ioctls work only for raw device nodes. */ char *tmp = getfullrawname(devNode); gLibHalFreeString(devNode); devNode = tmp; #endif if (devNode != 0) { // if (validateDevice(devNode, true)) // { Utf8Str description; char *vendor, *product; /* We do not check the error here, as this field may not even exist. */ vendor = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "info.vendor", 0); product = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "info.product", &dbusError); if ((product != 0 && product[0] != 0)) { if ((vendor != 0) && (vendor[0] != 0)) { description = Utf8StrFmt ("%s %s", vendor, product); } else { description = product; } ComObjPtr hostDVDDriveObj; hostDVDDriveObj.createObject(); hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(devNode), Bstr(description)); list.push_back (hostDVDDriveObj); } else { if (product == 0) { LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n", halDevices[i], dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } ComObjPtr hostDVDDriveObj; hostDVDDriveObj.createObject(); hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(devNode)); list.push_back (hostDVDDriveObj); } if (vendor != 0) { gLibHalFreeString(vendor); } if (product != 0) { gLibHalFreeString(product); } // } // else // { // LogRel(("Host::COMGETTER(DVDDrives): failed to validate the block device %s as a DVD drive\n")); // } #ifndef RT_OS_SOLARIS gLibHalFreeString(devNode); #else free(devNode); #endif } else { LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n", halDevices[i], dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } } gLibHalFreeStringArray(halDevices); } else { LogRel(("Host::COMGETTER(DVDDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */ { LogRel(("Host::COMGETTER(DVDDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } } else { LogRel(("Host::COMGETTER(DVDDrives): failed to initialise libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } gLibHalCtxFree(halContext); } else { LogRel(("Host::COMGETTER(DVDDrives): failed to set libhal connection to dbus.\n")); } } else { LogRel(("Host::COMGETTER(DVDDrives): failed to get a libhal context - out of memory?\n")); } gDBusConnectionUnref(dbusConnection); } else { LogRel(("Host::COMGETTER(DVDDrives): failed to connect to dbus. dbus error: %s (%s)\n", dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } return halSuccess; } /** * Helper function to query the hal subsystem for information about floppy drives attached to the * system. * * @returns true if information was successfully obtained, false otherwise * @retval list drives found will be attached to this list */ bool Host::getFloppyInfoFromHal(std::list< ComObjPtr > &list) { bool halSuccess = false; DBusError dbusError; if (!gLibHalCheckPresence()) return false; gDBusErrorInit (&dbusError); DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError); if (dbusConnection != 0) { LibHalContext *halContext = gLibHalCtxNew(); if (halContext != 0) { if (gLibHalCtxSetDBusConnection (halContext, dbusConnection)) { if (gLibHalCtxInit(halContext, &dbusError)) { int numDevices; char **halDevices = gLibHalFindDeviceStringMatch(halContext, "storage.drive_type", "floppy", &numDevices, &dbusError); if (halDevices != 0) { /* Hal is installed and working, so if no devices are reported, assume that there are none. */ halSuccess = true; for (int i = 0; i < numDevices; i++) { char *driveType = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "storage.drive_type", 0); if (driveType != 0) { if (strcmp(driveType, "floppy") != 0) { gLibHalFreeString(driveType); continue; } gLibHalFreeString(driveType); } else { /* An error occurred. The attribute "storage.drive_type" probably didn't exist. */ continue; } char *devNode = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "block.device", &dbusError); if (devNode != 0) { // if (validateDevice(devNode, false)) // { Utf8Str description; char *vendor, *product; /* We do not check the error here, as this field may not even exist. */ vendor = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "info.vendor", 0); product = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "info.product", &dbusError); if ((product != 0) && (product[0] != 0)) { if ((vendor != 0) && (vendor[0] != 0)) { description = Utf8StrFmt ("%s %s", vendor, product); } else { description = product; } ComObjPtr hostFloppyDrive; hostFloppyDrive.createObject(); hostFloppyDrive->init(m->pParent, DeviceType_DVD, Bstr(devNode), Bstr(description)); list.push_back (hostFloppyDrive); } else { if (product == 0) { LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n", halDevices[i], dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } ComObjPtr hostFloppyDrive; hostFloppyDrive.createObject(); hostFloppyDrive->init(m->pParent, DeviceType_DVD, Bstr(devNode)); list.push_back (hostFloppyDrive); } if (vendor != 0) { gLibHalFreeString(vendor); } if (product != 0) { gLibHalFreeString(product); } // } // else // { // LogRel(("Host::COMGETTER(FloppyDrives): failed to validate the block device %s as a floppy drive\n")); // } gLibHalFreeString(devNode); } else { LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n", halDevices[i], dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } } gLibHalFreeStringArray(halDevices); } else { LogRel(("Host::COMGETTER(FloppyDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */ { LogRel(("Host::COMGETTER(FloppyDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } } else { LogRel(("Host::COMGETTER(FloppyDrives): failed to initialise libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } gLibHalCtxFree(halContext); } else { LogRel(("Host::COMGETTER(FloppyDrives): failed to set libhal connection to dbus.\n")); } } else { LogRel(("Host::COMGETTER(FloppyDrives): failed to get a libhal context - out of memory?\n")); } gDBusConnectionUnref(dbusConnection); } else { LogRel(("Host::COMGETTER(FloppyDrives): failed to connect to dbus. dbus error: %s (%s)\n", dbusError.name, dbusError.message)); gDBusErrorFree(&dbusError); } return halSuccess; } #endif /* RT_OS_SOLARIS and VBOX_USE_HAL */ /** @todo get rid of dead code below - RT_OS_SOLARIS and RT_OS_LINUX are never both set */ #if defined(RT_OS_SOLARIS) /** * Helper function to parse the given mount file and add found entries */ void Host::parseMountTable(char *mountTable, std::list< ComObjPtr > &list) { #ifdef RT_OS_LINUX FILE *mtab = setmntent(mountTable, "r"); if (mtab) { struct mntent *mntent; char *mnt_type; char *mnt_dev; char *tmp; while ((mntent = getmntent(mtab))) { mnt_type = (char*)malloc(strlen(mntent->mnt_type) + 1); mnt_dev = (char*)malloc(strlen(mntent->mnt_fsname) + 1); strcpy(mnt_type, mntent->mnt_type); strcpy(mnt_dev, mntent->mnt_fsname); // supermount fs case if (strcmp(mnt_type, "supermount") == 0) { tmp = strstr(mntent->mnt_opts, "fs="); if (tmp) { free(mnt_type); mnt_type = strdup(tmp + strlen("fs=")); if (mnt_type) { tmp = strchr(mnt_type, ','); if (tmp) *tmp = '\0'; } } tmp = strstr(mntent->mnt_opts, "dev="); if (tmp) { free(mnt_dev); mnt_dev = strdup(tmp + strlen("dev=")); if (mnt_dev) { tmp = strchr(mnt_dev, ','); if (tmp) *tmp = '\0'; } } } // use strstr here to cover things fs types like "udf,iso9660" if (strstr(mnt_type, "iso9660") == 0) { /** @todo check whether we've already got the drive in our list! */ if (validateDevice(mnt_dev, true)) { ComObjPtr hostDVDDriveObj; hostDVDDriveObj.createObject(); hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(mnt_dev)); list.push_back (hostDVDDriveObj); } } free(mnt_dev); free(mnt_type); } endmntent(mtab); } #else // RT_OS_SOLARIS FILE *mntFile = fopen(mountTable, "r"); if (mntFile) { struct mnttab mntTab; while (getmntent(mntFile, &mntTab) == 0) { const char *mountName = mntTab.mnt_special; const char *mountPoint = mntTab.mnt_mountp; const char *mountFSType = mntTab.mnt_fstype; if (mountName && mountPoint && mountFSType) { // skip devices we are not interested in if ((*mountName && mountName[0] == '/') && // skip 'fake' devices (like -hosts, proc, fd, swap) (*mountFSType && (strncmp(mountFSType, "devfs", 5) != 0 && // skip devfs (i.e. /devices) strncmp(mountFSType, "dev", 3) != 0 && // skip dev (i.e. /dev) strncmp(mountFSType, "lofs", 4) != 0))) // skip loop-back file-system (lofs) { char *rawDevName = getfullrawname((char *)mountName); if (validateDevice(rawDevName, true)) { ComObjPtr hostDVDDriveObj; hostDVDDriveObj.createObject(); hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(rawDevName)); list.push_back (hostDVDDriveObj); } free(rawDevName); } } } fclose(mntFile); } #endif } /** * Helper function to check whether the given device node is a valid drive */ bool Host::validateDevice(const char *deviceNode, bool isCDROM) { struct stat statInfo; bool retValue = false; // sanity check if (!deviceNode) { return false; } // first a simple stat() call if (stat(deviceNode, &statInfo) < 0) { return false; } else { if (isCDROM) { if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode)) { int fileHandle; // now try to open the device fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0); if (fileHandle >= 0) { cdrom_subchnl cdChannelInfo; cdChannelInfo.cdsc_format = CDROM_MSF; // this call will finally reveal the whole truth #ifdef RT_OS_LINUX if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) || (errno == EIO) || (errno == ENOENT) || (errno == EINVAL) || (errno == ENOMEDIUM)) #else if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) || (errno == EIO) || (errno == ENOENT) || (errno == EINVAL)) #endif { retValue = true; } close(fileHandle); } } } else { // floppy case if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode)) { /// @todo do some more testing, maybe a nice IOCTL! retValue = true; } } } return retValue; } #endif // RT_OS_SOLARIS #ifdef VBOX_WITH_USB /** * Checks for the presence and status of the USB Proxy Service. * Returns S_OK when the Proxy is present and OK, VBOX_E_HOST_ERROR (as a * warning) if the proxy service is not available due to the way the host is * configured (at present, that means that usbfs and hal/DBus are not * available on a Linux host) or E_FAIL and a corresponding error message * otherwise. Intended to be used by methods that rely on the Proxy Service * availability. * * @note This method may return a warning result code. It is recommended to use * MultiError to store the return value. * * @note Locks this object for reading. */ HRESULT Host::checkUSBProxyService() { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); AssertReturn(m->pUSBProxyService, E_FAIL); if (!m->pUSBProxyService->isActive()) { /* disable the USB controller completely to avoid assertions if the * USB proxy service could not start. */ switch (m->pUSBProxyService->getLastError()) { case VERR_FILE_NOT_FOUND: /** @todo what does this mean? */ return setWarning(E_FAIL, tr("Could not load the Host USB Proxy Service (VERR_FILE_NOT_FOUND). The service might not be installed on the host computer")); case VERR_VUSB_USB_DEVICE_PERMISSION: return setWarning(E_FAIL, tr("VirtualBox is not currently allowed to access USB devices. You can change this by adding your user to the 'vboxusers' group. Please see the user manual for a more detailed explanation")); case VERR_VUSB_USBFS_PERMISSION: return setWarning(E_FAIL, tr("VirtualBox is not currently allowed to access USB devices. You can change this by allowing your user to access the 'usbfs' folder and files. Please see the user manual for a more detailed explanation")); case VINF_SUCCESS: return setWarning(E_FAIL, tr("The USB Proxy Service has not yet been ported to this host")); default: return setWarning (E_FAIL, "%s: %Rrc", tr ("Could not load the Host USB Proxy service"), m->pUSBProxyService->getLastError()); } } return S_OK; } #endif /* VBOX_WITH_USB */ #ifdef VBOX_WITH_RESOURCE_USAGE_API void Host::registerMetrics(PerformanceCollector *aCollector) { pm::CollectorHAL *hal = aCollector->getHAL(); /* Create sub metrics */ pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User", "Percentage of processor time spent in user mode."); pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel", "Percentage of processor time spent in kernel mode."); pm::SubMetric *cpuLoadIdle = new pm::SubMetric("CPU/Load/Idle", "Percentage of processor time spent idling."); pm::SubMetric *cpuMhzSM = new pm::SubMetric("CPU/MHz", "Average of current frequency of all processors."); pm::SubMetric *ramUsageTotal = new pm::SubMetric("RAM/Usage/Total", "Total physical memory installed."); pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used", "Physical memory currently occupied."); pm::SubMetric *ramUsageFree = new pm::SubMetric("RAM/Usage/Free", "Physical memory currently available to applications."); pm::SubMetric *ramVMMUsed = new pm::SubMetric("RAM/VMM/Used", "Total physical memory used by the hypervisor."); pm::SubMetric *ramVMMFree = new pm::SubMetric("RAM/VMM/Free", "Total physical memory free inside the hypervisor."); pm::SubMetric *ramVMMBallooned = new pm::SubMetric("RAM/VMM/Ballooned", "Total physical memory ballooned by the hypervisor."); pm::SubMetric *ramVMMShared = new pm::SubMetric("RAM/VMM/Shared", "Total physical memory shared between VMs."); /* Create and register base metrics */ IUnknown *objptr; ComObjPtr tmp = this; tmp.queryInterfaceTo(&objptr); pm::BaseMetric *cpuLoad = new pm::HostCpuLoadRaw(hal, objptr, cpuLoadUser, cpuLoadKernel, cpuLoadIdle); aCollector->registerBaseMetric (cpuLoad); pm::BaseMetric *cpuMhz = new pm::HostCpuMhz(hal, objptr, cpuMhzSM); aCollector->registerBaseMetric (cpuMhz); pm::BaseMetric *ramUsage = new pm::HostRamUsage(hal, objptr, ramUsageTotal, ramUsageUsed, ramUsageFree); aCollector->registerBaseMetric (ramUsage); pm::BaseMetric *ramVmm = new pm::HostRamVmm(aCollector->getGuestManager(), objptr, ramVMMUsed, ramVMMFree, ramVMMBallooned, ramVMMShared); aCollector->registerBaseMetric (ramVmm); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0)); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, new pm::AggregateMin())); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, new pm::AggregateMax())); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0)); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, new pm::AggregateMin())); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, new pm::AggregateMax())); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadIdle, 0)); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadIdle, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadIdle, new pm::AggregateMin())); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadIdle, new pm::AggregateMax())); aCollector->registerMetric(new pm::Metric(cpuMhz, cpuMhzSM, 0)); aCollector->registerMetric(new pm::Metric(cpuMhz, cpuMhzSM, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(cpuMhz, cpuMhzSM, new pm::AggregateMin())); aCollector->registerMetric(new pm::Metric(cpuMhz, cpuMhzSM, new pm::AggregateMax())); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageTotal, 0)); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageTotal, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageTotal, new pm::AggregateMin())); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageTotal, new pm::AggregateMax())); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0)); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, new pm::AggregateMin())); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, new pm::AggregateMax())); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageFree, 0)); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageFree, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageFree, new pm::AggregateMin())); aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageFree, new pm::AggregateMax())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMUsed, 0)); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMUsed, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMUsed, new pm::AggregateMin())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMUsed, new pm::AggregateMax())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMFree, 0)); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMFree, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMFree, new pm::AggregateMin())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMFree, new pm::AggregateMax())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMBallooned, 0)); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMBallooned, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMBallooned, new pm::AggregateMin())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMBallooned, new pm::AggregateMax())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMShared, 0)); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMShared, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMShared, new pm::AggregateMin())); aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMShared, new pm::AggregateMax())); } void Host::unregisterMetrics (PerformanceCollector *aCollector) { aCollector->unregisterMetricsFor(this); aCollector->unregisterBaseMetricsFor(this); } /* static */ void Host::generateMACAddress(Utf8Str &mac) { /* * Our strategy is as follows: the first three bytes are our fixed * vendor ID (080027). The remaining 3 bytes will be taken from the * start of a GUID. This is a fairly safe algorithm. */ Guid guid; guid.create(); mac = Utf8StrFmt("080027%02X%02X%02X", guid.raw()->au8[0], guid.raw()->au8[1], guid.raw()->au8[2]); } #endif /* VBOX_WITH_RESOURCE_USAGE_API */ /* vi: set tabstop=4 shiftwidth=4 expandtab: */