VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/HostImpl.cpp@ 95294

Last change on this file since 95294 was 95065, checked in by vboxsync, 3 years ago

Main/Host::i_updateProcessorFeatures: SUPR0QueryVTCaps may return VERR_SUP_DRIVERLESS in driverless mode, so just deal with it as best as we can. bugref:10138

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 149.5 KB
Line 
1/* $Id: HostImpl.cpp 95065 2022-05-23 20:06:47Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Host
4 */
5
6/*
7 * Copyright (C) 2004-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_HOST
19
20#define __STDC_LIMIT_MACROS
21#define __STDC_CONSTANT_MACROS
22
23// VBoxNetCfg-win.h needs winsock2.h and thus MUST be included before any other
24// header file includes Windows.h.
25#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT)
26# include <VBox/VBoxNetCfg-win.h>
27#endif
28
29// for some reason Windows burns in sdk\...\winsock.h if this isn't included first
30#include "VBox/com/ptr.h"
31
32#include "HostImpl.h"
33
34#ifdef VBOX_WITH_USB
35# include "HostUSBDeviceImpl.h"
36# include "USBDeviceFilterImpl.h"
37# include "USBProxyService.h"
38#else
39# include "VirtualBoxImpl.h"
40#endif // VBOX_WITH_USB
41
42#include "HostNetworkInterfaceImpl.h"
43#include "HostVideoInputDeviceImpl.h"
44#include "AutoCaller.h"
45#include "LoggingNew.h"
46#include "Performance.h"
47#ifdef VBOX_WITH_UPDATE_AGENT
48# include "UpdateAgentImpl.h"
49#endif
50
51#include "MediumImpl.h"
52#include "HostPower.h"
53
54#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
55# include <HostHardwareLinux.h>
56#endif
57
58#include <set>
59
60#ifdef VBOX_WITH_RESOURCE_USAGE_API
61# include "PerformanceImpl.h"
62#endif /* VBOX_WITH_RESOURCE_USAGE_API */
63
64#if defined(RT_OS_DARWIN) && ARCH_BITS == 32
65# include <sys/types.h>
66# include <sys/sysctl.h>
67#endif
68
69#ifdef RT_OS_LINUX
70# include <sys/ioctl.h>
71# include <errno.h>
72# include <net/if.h>
73# include <net/if_arp.h>
74#endif /* RT_OS_LINUX */
75
76#ifdef RT_OS_SOLARIS
77# include <fcntl.h>
78# include <unistd.h>
79# include <stropts.h>
80# include <errno.h>
81# include <limits.h>
82# include <stdio.h>
83# include <libdevinfo.h>
84# include <sys/mkdev.h>
85# include <sys/scsi/generic/inquiry.h>
86# include <net/if.h>
87# include <sys/socket.h>
88# include <sys/sockio.h>
89# include <net/if_arp.h>
90# include <net/if.h>
91# include <sys/types.h>
92# include <sys/stat.h>
93# include <sys/cdio.h>
94# include <sys/dkio.h>
95# include <sys/mnttab.h>
96# include <sys/mntent.h>
97/* Dynamic loading of libhal on Solaris hosts */
98# ifdef VBOX_USE_LIBHAL
99# include "vbox-libhal.h"
100extern "C" char *getfullrawname(char *);
101# endif
102# include "solaris/DynLoadLibSolaris.h"
103
104/**
105 * Solaris DVD drive list as returned by getDVDInfoFromDevTree().
106 */
107typedef struct SOLARISDVD
108{
109 struct SOLARISDVD *pNext;
110 char szDescription[512];
111 char szRawDiskPath[PATH_MAX];
112} SOLARISDVD;
113/** Pointer to a Solaris DVD descriptor. */
114typedef SOLARISDVD *PSOLARISDVD;
115
116/** Solaris fixed drive (SSD, HDD, ++) descriptor list entry as returned by the
117 * solarisWalkDeviceNodeForFixedDrive callback. */
118typedef SOLARISDVD SOLARISFIXEDDISK;
119/** Pointer to a Solaris fixed drive (SSD, HDD, ++) descriptor. */
120typedef SOLARISFIXEDDISK *PSOLARISFIXEDDISK;
121
122
123#endif /* RT_OS_SOLARIS */
124
125#ifdef RT_OS_WINDOWS
126# define _WIN32_DCOM
127# include <iprt/win/windows.h>
128# include <shellapi.h>
129# define INITGUID
130# include <guiddef.h>
131# include <devguid.h>
132# include <iprt/win/objbase.h>
133# include <iprt/win/shlobj.h>
134# include <cfgmgr32.h>
135# include <tchar.h>
136#endif /* RT_OS_WINDOWS */
137
138#ifdef RT_OS_DARWIN
139# include "darwin/iokit.h"
140#endif
141
142#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
143# include <iprt/asm-amd64-x86.h>
144#endif
145#ifdef RT_OS_SOLARIS
146# include <iprt/ctype.h>
147#endif
148#if defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS)
149# include <iprt/file.h>
150#endif
151#include <iprt/mp.h>
152#include <iprt/env.h>
153#include <iprt/mem.h>
154#include <iprt/param.h>
155#include <iprt/string.h>
156#include <iprt/system.h>
157#ifndef RT_OS_WINDOWS
158# include <iprt/path.h>
159#endif
160#include <iprt/time.h>
161#ifdef RT_OS_WINDOWS
162# include <iprt/dir.h>
163# include <iprt/vfs.h>
164#endif
165
166#ifdef VBOX_WITH_HOSTNETIF_API
167# include "netif.h"
168#endif
169
170#include <VBox/usb.h>
171#include <VBox/err.h>
172#include <VBox/settings.h>
173#include <VBox/sup.h>
174#ifdef VBOX_WITH_3D_ACCELERATION
175# include <VBox/VBoxOGL.h>
176#endif
177#include <iprt/x86.h>
178
179#include "VBox/com/MultiResult.h"
180#include "VBox/com/array.h"
181
182#include <stdio.h>
183
184#include <algorithm>
185#include <iprt/sanitized/string>
186#include <vector>
187
188#include "HostDnsService.h"
189#include "HostDriveImpl.h"
190#include "HostDrivePartitionImpl.h"
191
192////////////////////////////////////////////////////////////////////////////////
193//
194// Host private data definition
195//
196////////////////////////////////////////////////////////////////////////////////
197
198struct Host::Data
199{
200 Data()
201 :
202 fDVDDrivesListBuilt(false),
203 fFloppyDrivesListBuilt(false),
204 fPersistentConfigUpToDate(false)
205 {};
206
207 VirtualBox *pParent;
208
209 HostNetworkInterfaceList llNetIfs; // list of network interfaces
210
211#ifdef VBOX_WITH_USB
212 USBDeviceFilterList llChildren; // all USB device filters
213 USBDeviceFilterList llUSBDeviceFilters; // USB device filters in use by the USB proxy service
214
215 /** Pointer to the USBProxyService object. */
216 USBProxyService *pUSBProxyService;
217#endif /* VBOX_WITH_USB */
218
219 // list of host drives; lazily created by getDVDDrives() and getFloppyDrives(),
220 // and protected by the medium tree lock handle (including the bools).
221 MediaList llDVDDrives,
222 llFloppyDrives;
223 bool fDVDDrivesListBuilt,
224 fFloppyDrivesListBuilt;
225
226#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
227 /** Object with information about host drives */
228 VBoxMainDriveInfo hostDrives;
229#endif
230
231 /** @name Features that can be queried with GetProcessorFeature.
232 * @{ */
233 bool fVTSupported,
234 fLongModeSupported,
235 fPAESupported,
236 fNestedPagingSupported,
237 fUnrestrictedGuestSupported,
238 fNestedHWVirtSupported,
239 fVirtVmsaveVmload,
240 fRecheckVTSupported;
241
242 /** @} */
243
244 /** 3D hardware acceleration supported? Tristate, -1 meaning not probed. */
245 int f3DAccelerationSupported;
246
247 HostPowerService *pHostPowerService;
248 /** Host's DNS informaton fetching */
249 HostDnsMonitorProxy hostDnsMonitorProxy;
250
251 /** Startup syncing of persistent config in extra data */
252 bool fPersistentConfigUpToDate;
253
254#ifdef VBOX_WITH_UPDATE_AGENT
255 /** Reference to the host update agent. */
256 const ComObjPtr<HostUpdateAgent> pUpdateHost;
257#endif
258};
259
260
261////////////////////////////////////////////////////////////////////////////////
262//
263// Constructor / destructor
264//
265////////////////////////////////////////////////////////////////////////////////
266DEFINE_EMPTY_CTOR_DTOR(Host)
267
268HRESULT Host::FinalConstruct()
269{
270 return BaseFinalConstruct();
271}
272
273void Host::FinalRelease()
274{
275 uninit();
276 BaseFinalRelease();
277}
278
279/**
280 * Initializes the host object.
281 *
282 * @param aParent VirtualBox parent object.
283 */
284HRESULT Host::init(VirtualBox *aParent)
285{
286 HRESULT hrc;
287 LogFlowThisFunc(("aParent=%p\n", aParent));
288
289 /* Enclose the state transition NotReady->InInit->Ready */
290 AutoInitSpan autoInitSpan(this);
291 AssertReturn(autoInitSpan.isOk(), E_FAIL);
292
293 m = new Data();
294
295 m->pParent = aParent;
296
297#ifdef VBOX_WITH_USB
298 /*
299 * Create and initialize the USB Proxy Service.
300 */
301 m->pUSBProxyService = new USBProxyService(this);
302 hrc = m->pUSBProxyService->init();
303 AssertComRCReturn(hrc, hrc);
304#endif /* VBOX_WITH_USB */
305
306#ifdef VBOX_WITH_RESOURCE_USAGE_API
307 i_registerMetrics(aParent->i_performanceCollector());
308#endif /* VBOX_WITH_RESOURCE_USAGE_API */
309 /* Create the list of network interfaces so their metrics get registered. */
310 i_updateNetIfList();
311
312 m->hostDnsMonitorProxy.init(m->pParent);
313
314#ifdef VBOX_WITH_UPDATE_AGENT
315 hrc = unconst(m->pUpdateHost).createObject();
316 if (SUCCEEDED(hrc))
317 hrc = m->pUpdateHost->init(m->pParent);
318 AssertComRCReturn(hrc, hrc);
319#endif
320
321#if defined(RT_OS_WINDOWS)
322 m->pHostPowerService = new HostPowerServiceWin(m->pParent);
323#elif defined(RT_OS_LINUX) && defined(VBOX_WITH_DBUS)
324 m->pHostPowerService = new HostPowerServiceLinux(m->pParent);
325#elif defined(RT_OS_DARWIN)
326 m->pHostPowerService = new HostPowerServiceDarwin(m->pParent);
327#else
328 m->pHostPowerService = new HostPowerService(m->pParent);
329#endif
330
331 /* Cache the features reported by GetProcessorFeature. */
332 m->fVTSupported = false;
333 m->fLongModeSupported = false;
334 m->fPAESupported = false;
335 m->fNestedPagingSupported = false;
336 m->fUnrestrictedGuestSupported = false;
337 m->fNestedHWVirtSupported = false;
338 m->fVirtVmsaveVmload = false;
339 m->fRecheckVTSupported = false;
340
341#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
342 if (ASMHasCpuId())
343 {
344 /* Note! This code is duplicated in SUPDrv.c and other places! */
345 uint32_t uMaxId, uVendorEBX, uVendorECX, uVendorEDX;
346 ASMCpuId(0, &uMaxId, &uVendorEBX, &uVendorECX, &uVendorEDX);
347 if (RTX86IsValidStdRange(uMaxId))
348 {
349 /* PAE? */
350 uint32_t uDummy, fFeaturesEcx, fFeaturesEdx;
351 ASMCpuId(1, &uDummy, &uDummy, &fFeaturesEcx, &fFeaturesEdx);
352 m->fPAESupported = RT_BOOL(fFeaturesEdx & X86_CPUID_FEATURE_EDX_PAE);
353
354 /* Long Mode? */
355 uint32_t uExtMaxId, fExtFeaturesEcx, fExtFeaturesEdx;
356 ASMCpuId(0x80000000, &uExtMaxId, &uDummy, &uDummy, &uDummy);
357 ASMCpuId(0x80000001, &uDummy, &uDummy, &fExtFeaturesEcx, &fExtFeaturesEdx);
358 m->fLongModeSupported = RTX86IsValidExtRange(uExtMaxId)
359 && (fExtFeaturesEdx & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE);
360
361# if defined(RT_OS_DARWIN) && ARCH_BITS == 32 /* darwin.x86 has some optimizations of 64-bit on 32-bit. */
362 int f64bitCapable = 0;
363 size_t cbParameter = sizeof(f64bitCapable);
364 if (sysctlbyname("hw.cpu64bit_capable", &f64bitCapable, &cbParameter, NULL, NULL) != -1)
365 m->fLongModeSupported = f64bitCapable != 0;
366# endif
367
368 /* VT-x? */
369 if ( RTX86IsIntelCpu(uVendorEBX, uVendorECX, uVendorEDX)
370 || RTX86IsViaCentaurCpu(uVendorEBX, uVendorECX, uVendorEDX)
371 || RTX86IsShanghaiCpu(uVendorEBX, uVendorECX, uVendorEDX))
372 {
373 if ( (fFeaturesEcx & X86_CPUID_FEATURE_ECX_VMX)
374 && (fFeaturesEdx & X86_CPUID_FEATURE_EDX_MSR)
375 && (fFeaturesEdx & X86_CPUID_FEATURE_EDX_FXSR)
376 )
377 {
378 const char *pszIgn;
379 int rc = SUPR3QueryVTxSupported(&pszIgn);
380 if (RT_SUCCESS(rc))
381 m->fVTSupported = true;
382 }
383 }
384 /* AMD-V */
385 else if ( RTX86IsAmdCpu(uVendorEBX, uVendorECX, uVendorEDX)
386 || RTX86IsHygonCpu(uVendorEBX, uVendorECX, uVendorEDX))
387 {
388 if ( (fExtFeaturesEcx & X86_CPUID_AMD_FEATURE_ECX_SVM)
389 && (fFeaturesEdx & X86_CPUID_FEATURE_EDX_MSR)
390 && (fFeaturesEdx & X86_CPUID_FEATURE_EDX_FXSR)
391 && RTX86IsValidExtRange(uExtMaxId)
392 )
393 {
394 m->fVTSupported = true;
395 m->fUnrestrictedGuestSupported = true;
396
397 /* Query AMD features. */
398 if (uExtMaxId >= 0x8000000a)
399 {
400 uint32_t fSVMFeaturesEdx;
401 ASMCpuId(0x8000000a, &uDummy, &uDummy, &uDummy, &fSVMFeaturesEdx);
402 if (fSVMFeaturesEdx & X86_CPUID_SVM_FEATURE_EDX_NESTED_PAGING)
403 m->fNestedPagingSupported = true;
404 if (fSVMFeaturesEdx & X86_CPUID_SVM_FEATURE_EDX_VIRT_VMSAVE_VMLOAD)
405 m->fVirtVmsaveVmload = true;
406 }
407 }
408 }
409 }
410 }
411#endif /* defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) */
412
413
414 /* Check with SUPDrv if VT-x and AMD-V are really supported (may fail). */
415 if (m->fVTSupported)
416 {
417 m->fRecheckVTSupported = true; /* Try again later when the driver is loaded; cleared by i_updateProcessorFeatures on success. */
418 i_updateProcessorFeatures();
419 }
420
421 /* Check for NEM in root paritition (hyper-V / windows). */
422 if (!m->fVTSupported && SUPR3IsNemSupportedWhenNoVtxOrAmdV())
423 {
424 m->fVTSupported = m->fNestedPagingSupported = true;
425 m->fRecheckVTSupported = false;
426 }
427
428#ifdef VBOX_WITH_3D_ACCELERATION
429 /* Test for 3D hardware acceleration support later when (if ever) need. */
430 m->f3DAccelerationSupported = -1;
431#else
432 m->f3DAccelerationSupported = false;
433#endif
434
435#if defined(VBOX_WITH_HOSTNETIF_API) && (defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD))
436 /* Extract the list of configured host-only interfaces */
437 std::set<Utf8Str> aConfiguredNames;
438 SafeArray<BSTR> aGlobalExtraDataKeys;
439 hrc = aParent->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys));
440 AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc));
441 for (size_t i = 0; i < aGlobalExtraDataKeys.size(); ++i)
442 {
443 Utf8Str strKey = aGlobalExtraDataKeys[i];
444
445 if (!strKey.startsWith("HostOnly/vboxnet"))
446 continue;
447
448 size_t pos = strKey.find("/", sizeof("HostOnly/vboxnet"));
449 if (pos != Utf8Str::npos)
450 aConfiguredNames.insert(strKey.substr(sizeof("HostOnly"),
451 pos - sizeof("HostOnly")));
452 }
453
454 for (std::set<Utf8Str>::const_iterator it = aConfiguredNames.begin();
455 it != aConfiguredNames.end();
456 ++it)
457 {
458 ComPtr<IHostNetworkInterface> hif;
459 ComPtr<IProgress> progress;
460
461 int vrc = NetIfCreateHostOnlyNetworkInterface(m->pParent,
462 hif.asOutParam(),
463 progress.asOutParam(),
464 it->c_str());
465 if (RT_FAILURE(vrc))
466 LogRel(("failed to create %s, error (%Rrc)\n", it->c_str(), vrc));
467 }
468
469#endif /* defined(VBOX_WITH_HOSTNETIF_API) && (defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)) */
470
471 /* Confirm a successful initialization */
472 autoInitSpan.setSucceeded();
473
474 return S_OK;
475}
476
477/**
478 * Uninitializes the host object and sets the ready flag to FALSE.
479 * Called either from FinalRelease() or by the parent when it gets destroyed.
480 */
481void Host::uninit()
482{
483 LogFlowThisFunc(("\n"));
484
485 /* Enclose the state transition Ready->InUninit->NotReady */
486 AutoUninitSpan autoUninitSpan(this);
487 if (autoUninitSpan.uninitDone())
488 return;
489
490#ifdef VBOX_WITH_RESOURCE_USAGE_API
491 PerformanceCollector *aCollector = m->pParent->i_performanceCollector();
492 i_unregisterMetrics(aCollector);
493#endif /* VBOX_WITH_RESOURCE_USAGE_API */
494 /*
495 * Note that unregisterMetrics() has unregistered all metrics associated
496 * with Host including network interface ones. We can destroy network
497 * interface objects now. Don't forget the uninit call, otherwise this
498 * causes a race with crashing API clients getting their stale references
499 * cleaned up and VirtualBox shutting down.
500 */
501 while (!m->llNetIfs.empty())
502 {
503 ComObjPtr<HostNetworkInterface> &pNet = m->llNetIfs.front();
504 pNet->uninit();
505 m->llNetIfs.pop_front();
506 }
507
508 m->hostDnsMonitorProxy.uninit();
509
510#ifdef VBOX_WITH_UPDATE_AGENT
511 if (m->pUpdateHost)
512 {
513 m->pUpdateHost->uninit();
514 unconst(m->pUpdateHost).setNull();
515 }
516#endif
517
518#ifdef VBOX_WITH_USB
519 /* wait for USB proxy service to terminate before we uninit all USB
520 * devices */
521 LogFlowThisFunc(("Stopping USB proxy service...\n"));
522 delete m->pUSBProxyService;
523 m->pUSBProxyService = NULL;
524 LogFlowThisFunc(("Done stopping USB proxy service.\n"));
525#endif
526
527 delete m->pHostPowerService;
528
529#ifdef VBOX_WITH_USB
530 /* uninit all USB device filters still referenced by clients
531 * Note! HostUSBDeviceFilter::uninit() will modify llChildren.
532 * This list should be already empty, but better be safe than sorry. */
533 while (!m->llChildren.empty())
534 {
535 ComObjPtr<HostUSBDeviceFilter> &pChild = m->llChildren.front();
536 pChild->uninit();
537 m->llChildren.pop_front();
538 }
539
540 /* No need to uninit these, as either Machine::uninit() or the above loop
541 * already covered them all. Subset of llChildren. */
542 m->llUSBDeviceFilters.clear();
543#endif
544
545 /* uninit all host DVD medium objects */
546 while (!m->llDVDDrives.empty())
547 {
548 ComObjPtr<Medium> &pMedium = m->llDVDDrives.front();
549 pMedium->uninit();
550 m->llDVDDrives.pop_front();
551 }
552 /* uninit all host floppy medium objects */
553 while (!m->llFloppyDrives.empty())
554 {
555 ComObjPtr<Medium> &pMedium = m->llFloppyDrives.front();
556 pMedium->uninit();
557 m->llFloppyDrives.pop_front();
558 }
559
560 delete m;
561 m = NULL;
562}
563
564////////////////////////////////////////////////////////////////////////////////
565//
566// IHost public methods
567//
568////////////////////////////////////////////////////////////////////////////////
569
570/**
571 * Returns a list of host DVD drives.
572 *
573 * @returns COM status code
574 * @param aDVDDrives address of result pointer
575 */
576
577HRESULT Host::getDVDDrives(std::vector<ComPtr<IMedium> > &aDVDDrives)
578{
579 AutoWriteLock treeLock(m->pParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
580
581 MediaList *pList;
582 HRESULT rc = i_getDrives(DeviceType_DVD, true /* fRefresh */, pList, treeLock);
583 if (FAILED(rc))
584 return rc;
585
586 aDVDDrives.resize(pList->size());
587 size_t i = 0;
588 for (MediaList::const_iterator it = pList->begin(); it != pList->end(); ++it, ++i)
589 (*it).queryInterfaceTo(aDVDDrives[i].asOutParam());
590
591 return S_OK;
592}
593
594/**
595 * Returns a list of host floppy drives.
596 *
597 * @returns COM status code
598 * @param aFloppyDrives address of result pointer
599 */
600HRESULT Host::getFloppyDrives(std::vector<ComPtr<IMedium> > &aFloppyDrives)
601{
602 AutoWriteLock treeLock(m->pParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
603
604 MediaList *pList;
605 HRESULT rc = i_getDrives(DeviceType_Floppy, true /* fRefresh */, pList, treeLock);
606 if (FAILED(rc))
607 return rc;
608
609 aFloppyDrives.resize(pList->size());
610 size_t i = 0;
611 for (MediaList::const_iterator it = pList->begin(); it != pList->end(); ++it, ++i)
612 (*it).queryInterfaceTo(aFloppyDrives[i].asOutParam());
613
614 return S_OK;
615}
616
617
618#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT)
619# define VBOX_APP_NAME L"VirtualBox"
620
621static int vboxNetWinAddComponent(std::list< ComObjPtr<HostNetworkInterface> > *pPist,
622 INetCfgComponent *pncc)
623{
624 LPWSTR lpszName;
625 GUID IfGuid;
626 HRESULT hr;
627 int rc = VERR_GENERAL_FAILURE;
628
629 hr = pncc->GetDisplayName(&lpszName);
630 Assert(hr == S_OK);
631 if (hr == S_OK)
632 {
633 Bstr name((CBSTR)lpszName);
634
635 hr = pncc->GetInstanceGuid(&IfGuid);
636 Assert(hr == S_OK);
637 if (hr == S_OK)
638 {
639 /* create a new object and add it to the list */
640 ComObjPtr<HostNetworkInterface> iface;
641 iface.createObject();
642 /* remove the curly bracket at the end */
643 if (SUCCEEDED(iface->init(name, name, Guid(IfGuid), HostNetworkInterfaceType_Bridged)))
644 {
645// iface->setVirtualBox(m->pParent);
646 pPist->push_back(iface);
647 rc = VINF_SUCCESS;
648 }
649 else
650 {
651 Assert(0);
652 }
653 }
654 CoTaskMemFree(lpszName);
655 }
656
657 return rc;
658}
659#endif /* defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT) */
660
661#if defined(RT_OS_WINDOWS)
662struct HostOnlyInfo
663{
664 HostOnlyInfo() : fDhcpEnabled(false), uIPv6PrefixLength(0) {};
665
666 Bstr bstrName;
667 bool fDhcpEnabled;
668 Bstr strIPv4Address;
669 Bstr strIPv4NetMask;
670 Bstr strIPv6Address;
671 ULONG uIPv6PrefixLength;
672};
673
674typedef std::map<Utf8Str, HostOnlyInfo*> GUID_TO_HOST_ONLY_INFO;
675
676HRESULT Host::i_updatePersistentConfigForHostOnlyAdapters(void)
677{
678 /* No need to do the sync twice */
679 if (m->fPersistentConfigUpToDate)
680 return S_OK;
681 m->fPersistentConfigUpToDate = true;
682 bool fChangesMade = false;
683
684 /* Extract the list of configured host-only interfaces */
685 GUID_TO_HOST_ONLY_INFO aSavedAdapters;
686 SafeArray<BSTR> aGlobalExtraDataKeys;
687 HRESULT hrc = m->pParent->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys));
688 AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc));
689 for (size_t i = 0; i < aGlobalExtraDataKeys.size(); ++i)
690 {
691 Utf8Str strKey = aGlobalExtraDataKeys[i];
692
693 if (strKey.startsWith("HostOnly/{"))
694 {
695 Bstr bstrValue;
696 hrc = m->pParent->GetExtraData(aGlobalExtraDataKeys[i], bstrValue.asOutParam());
697 if (hrc != S_OK)
698 continue;
699
700 Utf8Str strGuid = strKey.substr(10, 36); /* Skip "HostOnly/{" */
701 if (aSavedAdapters.find(strGuid) == aSavedAdapters.end())
702 aSavedAdapters[strGuid] = new HostOnlyInfo();
703
704 if (strKey.endsWith("}/Name"))
705 aSavedAdapters[strGuid]->bstrName = bstrValue;
706 else if (strKey.endsWith("}/IPAddress"))
707 {
708 if (bstrValue == "DHCP")
709 aSavedAdapters[strGuid]->fDhcpEnabled = true;
710 else
711 aSavedAdapters[strGuid]->strIPv4Address = bstrValue;
712 }
713 else if (strKey.endsWith("}/IPNetMask"))
714 aSavedAdapters[strGuid]->strIPv4NetMask = bstrValue;
715 else if (strKey.endsWith("}/IPV6Address"))
716 aSavedAdapters[strGuid]->strIPv6Address = bstrValue;
717 else if (strKey.endsWith("}/IPV6PrefixLen"))
718 aSavedAdapters[strGuid]->uIPv6PrefixLength = Utf8Str(bstrValue).toUInt32();
719 }
720 }
721
722 /* Go over the list of existing adapters and update configs saved in extra data */
723 std::set<Bstr> aKnownNames;
724 for (HostNetworkInterfaceList::iterator it = m->llNetIfs.begin(); it != m->llNetIfs.end(); ++it)
725 {
726 /* Get type */
727 HostNetworkInterfaceType_T t;
728 hrc = (*it)->COMGETTER(InterfaceType)(&t);
729 if (FAILED(hrc) || t != HostNetworkInterfaceType_HostOnly)
730 continue;
731 /* Get id */
732 Bstr bstrGuid;
733 hrc = (*it)->COMGETTER(Id)(bstrGuid.asOutParam());
734 if (FAILED(hrc))
735 continue;
736 /* Get name */
737 Bstr bstrName;
738 hrc = (*it)->COMGETTER(Name)(bstrName.asOutParam());
739 if (FAILED(hrc))
740 continue;
741
742 /* Remove adapter from map as it does not need any further processing */
743 aSavedAdapters.erase(Utf8Str(bstrGuid));
744 /* Add adapter name to the list of known names, so we won't attempt to create adapters with the same name */
745 aKnownNames.insert(bstrName);
746 /* Make sure our extra data contains the latest config */
747 hrc = (*it)->i_updatePersistentConfig();
748 if (hrc != S_OK)
749 break;
750 }
751
752 /* The following loop not only creates missing adapters, it destroys HostOnlyInfo objects contained in the map as well */
753 for (GUID_TO_HOST_ONLY_INFO::iterator it = aSavedAdapters.begin(); it != aSavedAdapters.end(); ++it)
754 {
755 Utf8Str strGuid = (*it).first;
756 HostOnlyInfo *pInfo = (*it).second;
757 /* We create adapters only if we haven't seen one with the same name */
758 if (aKnownNames.find(pInfo->bstrName) == aKnownNames.end())
759 {
760 /* There is no adapter with such name yet, create it */
761 ComPtr<IHostNetworkInterface> hif;
762 ComPtr<IProgress> progress;
763
764 int vrc = NetIfCreateHostOnlyNetworkInterface(m->pParent, hif.asOutParam(), progress.asOutParam(),
765 pInfo->bstrName.raw());
766 if (RT_FAILURE(vrc))
767 {
768 LogRel(("Failed to create host-only adapter (%Rrc)\n", vrc));
769 hrc = E_UNEXPECTED;
770 break;
771 }
772
773 /* Wait for the adapter to get configured completely, before we modify IP addresses. */
774 progress->WaitForCompletion(-1);
775 fChangesMade = true;
776 if (pInfo->fDhcpEnabled)
777 {
778 hrc = hif->EnableDynamicIPConfig();
779 if (FAILED(hrc))
780 LogRel(("EnableDynamicIPConfig failed with 0x%x\n", hrc));
781 }
782 else
783 {
784 hrc = hif->EnableStaticIPConfig(pInfo->strIPv4Address.raw(), pInfo->strIPv4NetMask.raw());
785 if (FAILED(hrc))
786 LogRel(("EnableStaticIpConfig failed with 0x%x\n", hrc));
787 }
788# if 0
789 /* Somehow HostNetworkInterface::EnableStaticIPConfigV6 is not implemented yet. */
790 if (SUCCEEDED(hrc))
791 {
792 hrc = hif->EnableStaticIPConfigV6(pInfo->strIPv6Address.raw(), pInfo->uIPv6PrefixLength);
793 if (FAILED(hrc))
794 LogRel(("EnableStaticIPConfigV6 failed with 0x%x\n", hrc));
795 }
796# endif
797 /* Now we have seen this name */
798 aKnownNames.insert(pInfo->bstrName);
799 /* Drop the old config as the newly created adapter has different GUID */
800 i_removePersistentConfig(strGuid);
801 }
802 delete pInfo;
803 }
804 /* Update the list again if we have created some adapters */
805 if (SUCCEEDED(hrc) && fChangesMade)
806 hrc = i_updateNetIfList();
807
808 return hrc;
809}
810#endif /* defined(RT_OS_WINDOWS) */
811
812/**
813 * Returns a list of host network interfaces.
814 *
815 * @returns COM status code
816 * @param aNetworkInterfaces address of result pointer
817 */
818HRESULT Host::getNetworkInterfaces(std::vector<ComPtr<IHostNetworkInterface> > &aNetworkInterfaces)
819{
820#if defined(RT_OS_WINDOWS) || defined(VBOX_WITH_NETFLT) /*|| defined(RT_OS_OS2)*/
821# ifdef VBOX_WITH_HOSTNETIF_API
822 HRESULT rc = i_updateNetIfList();
823 if (FAILED(rc))
824 {
825 Log(("Failed to update host network interface list with rc=%Rhrc\n", rc));
826 return rc;
827 }
828#if defined(RT_OS_WINDOWS)
829 rc = i_updatePersistentConfigForHostOnlyAdapters();
830 if (FAILED(rc))
831 {
832 LogRel(("Failed to update persistent config for host-only adapters with rc=%Rhrc\n", rc));
833 return rc;
834 }
835#endif /* defined(RT_OS_WINDOWS) */
836
837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
838
839 aNetworkInterfaces.resize(m->llNetIfs.size());
840 size_t i = 0;
841 for (HostNetworkInterfaceList::iterator it = m->llNetIfs.begin(); it != m->llNetIfs.end(); ++it, ++i)
842 (*it).queryInterfaceTo(aNetworkInterfaces[i].asOutParam());
843
844 return S_OK;
845# else
846 std::list<ComObjPtr<HostNetworkInterface> > list;
847
848# if defined(RT_OS_DARWIN)
849 PDARWINETHERNIC pEtherNICs = DarwinGetEthernetControllers();
850 while (pEtherNICs)
851 {
852 ComObjPtr<HostNetworkInterface> IfObj;
853 IfObj.createObject();
854 if (SUCCEEDED(IfObj->init(Bstr(pEtherNICs->szName), Guid(pEtherNICs->Uuid), HostNetworkInterfaceType_Bridged)))
855 list.push_back(IfObj);
856
857 /* next, free current */
858 void *pvFree = pEtherNICs;
859 pEtherNICs = pEtherNICs->pNext;
860 RTMemFree(pvFree);
861 }
862
863# elif defined RT_OS_WINDOWS
864# ifndef VBOX_WITH_NETFLT
865 hr = E_NOTIMPL;
866# else /* # if defined VBOX_WITH_NETFLT */
867 INetCfg *pNc;
868 INetCfgComponent *pMpNcc;
869 INetCfgComponent *pTcpIpNcc;
870 LPWSTR lpszApp;
871 HRESULT hr;
872 IEnumNetCfgBindingPath *pEnumBp;
873 INetCfgBindingPath *pBp;
874 IEnumNetCfgBindingInterface *pEnumBi;
875 INetCfgBindingInterface *pBi;
876
877 /* we are using the INetCfg API for getting the list of miniports */
878 hr = VBoxNetCfgWinQueryINetCfg(FALSE,
879 VBOX_APP_NAME,
880 &pNc,
881 &lpszApp);
882 Assert(hr == S_OK);
883 if (hr == S_OK)
884 {
885# ifdef VBOX_NETFLT_ONDEMAND_BIND
886 /* for the protocol-based approach for now we just get all miniports the MS_TCPIP protocol binds to */
887 hr = pNc->FindComponent(L"MS_TCPIP", &pTcpIpNcc);
888# else
889 /* for the filter-based approach we get all miniports our filter (oracle_VBoxNetLwf)is bound to */
890 hr = pNc->FindComponent(L"oracle_VBoxNetLwf", &pTcpIpNcc);
891 if (hr != S_OK)
892 {
893 /* fall back to NDIS5 miniport lookup (sun_VBoxNetFlt) */
894 hr = pNc->FindComponent(L"sun_VBoxNetFlt", &pTcpIpNcc);
895 }
896# ifndef VBOX_WITH_HARDENING
897 if (hr != S_OK)
898 {
899 /** @todo try to install the netflt from here */
900 }
901# endif
902
903# endif
904
905 if (hr == S_OK)
906 {
907 hr = VBoxNetCfgWinGetBindingPathEnum(pTcpIpNcc, EBP_BELOW, &pEnumBp);
908 Assert(hr == S_OK);
909 if (hr == S_OK)
910 {
911 hr = VBoxNetCfgWinGetFirstBindingPath(pEnumBp, &pBp);
912 Assert(hr == S_OK || hr == S_FALSE);
913 while (hr == S_OK)
914 {
915 /* S_OK == enabled, S_FALSE == disabled */
916 if (pBp->IsEnabled() == S_OK)
917 {
918 hr = VBoxNetCfgWinGetBindingInterfaceEnum(pBp, &pEnumBi);
919 Assert(hr == S_OK);
920 if (hr == S_OK)
921 {
922 hr = VBoxNetCfgWinGetFirstBindingInterface(pEnumBi, &pBi);
923 Assert(hr == S_OK);
924 while (hr == S_OK)
925 {
926 hr = pBi->GetLowerComponent(&pMpNcc);
927 Assert(hr == S_OK);
928 if (hr == S_OK)
929 {
930 ULONG uComponentStatus;
931 hr = pMpNcc->GetDeviceStatus(&uComponentStatus);
932 Assert(hr == S_OK);
933 if (hr == S_OK)
934 {
935 if (uComponentStatus == 0)
936 {
937 vboxNetWinAddComponent(&list, pMpNcc);
938 }
939 }
940 VBoxNetCfgWinReleaseRef(pMpNcc);
941 }
942 VBoxNetCfgWinReleaseRef(pBi);
943
944 hr = VBoxNetCfgWinGetNextBindingInterface(pEnumBi, &pBi);
945 }
946 VBoxNetCfgWinReleaseRef(pEnumBi);
947 }
948 }
949 VBoxNetCfgWinReleaseRef(pBp);
950
951 hr = VBoxNetCfgWinGetNextBindingPath(pEnumBp, &pBp);
952 }
953 VBoxNetCfgWinReleaseRef(pEnumBp);
954 }
955 VBoxNetCfgWinReleaseRef(pTcpIpNcc);
956 }
957 else
958 {
959 LogRel(("failed to get the oracle_VBoxNetLwf(sun_VBoxNetFlt) component, error (0x%x)\n", hr));
960 }
961
962 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE);
963 }
964# endif /* # if defined VBOX_WITH_NETFLT */
965
966
967# elif defined RT_OS_LINUX
968 int sock = socket(AF_INET, SOCK_DGRAM, 0);
969 if (sock >= 0)
970 {
971 char pBuffer[2048];
972 struct ifconf ifConf;
973 ifConf.ifc_len = sizeof(pBuffer);
974 ifConf.ifc_buf = pBuffer;
975 if (ioctl(sock, SIOCGIFCONF, &ifConf) >= 0)
976 {
977 for (struct ifreq *pReq = ifConf.ifc_req; (char*)pReq < pBuffer + ifConf.ifc_len; pReq++)
978 {
979 if (ioctl(sock, SIOCGIFHWADDR, pReq) >= 0)
980 {
981 if (pReq->ifr_hwaddr.sa_family == ARPHRD_ETHER)
982 {
983 RTUUID uuid;
984 Assert(sizeof(uuid) <= sizeof(*pReq));
985 memcpy(&uuid, pReq, sizeof(uuid));
986
987 ComObjPtr<HostNetworkInterface> IfObj;
988 IfObj.createObject();
989 if (SUCCEEDED(IfObj->init(Bstr(pReq->ifr_name), Guid(uuid), HostNetworkInterfaceType_Bridged)))
990 list.push_back(IfObj);
991 }
992 }
993 }
994 }
995 close(sock);
996 }
997# endif /* RT_OS_LINUX */
998
999 aNetworkInterfaces.resize(list.size());
1000 size_t i = 0;
1001 for (std::list<ComObjPtr<HostNetworkInterface> >::const_iterator it = list.begin(); it != list.end(); ++it, ++i)
1002 aNetworkInterfaces[i] = *it;
1003
1004 return S_OK;
1005# endif
1006#else
1007 /* Not implemented / supported on this platform. */
1008 RT_NOREF(aNetworkInterfaces);
1009 ReturnComNotImplemented();
1010#endif
1011}
1012
1013HRESULT Host::getUSBDevices(std::vector<ComPtr<IHostUSBDevice> > &aUSBDevices)
1014{
1015#ifdef VBOX_WITH_USB
1016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1017
1018 MultiResult rc = i_checkUSBProxyService();
1019 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
1020 return rc;
1021
1022 return m->pUSBProxyService->getDeviceCollection(aUSBDevices);
1023#else
1024 /* Note: The GUI depends on this method returning E_NOTIMPL with no
1025 * extended error info to indicate that USB is simply not available
1026 * (w/o treating it as a failure), for example, as in OSE. */
1027 RT_NOREF(aUSBDevices);
1028 ReturnComNotImplemented();
1029#endif
1030}
1031
1032/**
1033 * This method return the list of registered name servers
1034 */
1035HRESULT Host::getNameServers(std::vector<com::Utf8Str> &aNameServers)
1036{
1037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1038 return m->hostDnsMonitorProxy.GetNameServers(aNameServers);
1039}
1040
1041
1042/**
1043 * This method returns the domain name of the host
1044 */
1045HRESULT Host::getDomainName(com::Utf8Str &aDomainName)
1046{
1047 /* XXX: note here should be synchronization with thread polling state
1048 * changes in name resoving system on host */
1049 return m->hostDnsMonitorProxy.GetDomainName(&aDomainName);
1050}
1051
1052
1053/**
1054 * This method returns the search string.
1055 */
1056HRESULT Host::getSearchStrings(std::vector<com::Utf8Str> &aSearchStrings)
1057{
1058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1059 return m->hostDnsMonitorProxy.GetSearchStrings(aSearchStrings);
1060}
1061
1062HRESULT Host::getUSBDeviceFilters(std::vector<ComPtr<IHostUSBDeviceFilter> > &aUSBDeviceFilters)
1063{
1064#ifdef VBOX_WITH_USB
1065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1066
1067 MultiResult rc = i_checkUSBProxyService();
1068 if (FAILED(rc))
1069 return rc;
1070
1071 aUSBDeviceFilters.resize(m->llUSBDeviceFilters.size());
1072 size_t i = 0;
1073 for (USBDeviceFilterList::iterator it = m->llUSBDeviceFilters.begin(); it != m->llUSBDeviceFilters.end(); ++it, ++i)
1074 (*it).queryInterfaceTo(aUSBDeviceFilters[i].asOutParam());
1075
1076 return rc;
1077#else
1078 /* Note: The GUI depends on this method returning E_NOTIMPL with no
1079 * extended error info to indicate that USB is simply not available
1080 * (w/o treating it as a failure), for example, as in OSE. */
1081 RT_NOREF(aUSBDeviceFilters);
1082 ReturnComNotImplemented();
1083#endif
1084}
1085
1086/**
1087 * Returns the number of installed logical processors
1088 *
1089 * @returns COM status code
1090 * @param aCount address of result variable
1091 */
1092
1093HRESULT Host::getProcessorCount(ULONG *aCount)
1094{
1095 // no locking required
1096
1097 *aCount = RTMpGetPresentCount();
1098 return S_OK;
1099}
1100
1101/**
1102 * Returns the number of online logical processors
1103 *
1104 * @returns COM status code
1105 * @param aCount address of result variable
1106 */
1107HRESULT Host::getProcessorOnlineCount(ULONG *aCount)
1108{
1109 // no locking required
1110
1111 *aCount = RTMpGetOnlineCount();
1112 return S_OK;
1113}
1114
1115/**
1116 * Returns the number of installed physical processor cores.
1117 *
1118 * @returns COM status code
1119 * @param aCount address of result variable
1120 */
1121HRESULT Host::getProcessorCoreCount(ULONG *aCount)
1122{
1123 // no locking required
1124
1125 *aCount = RTMpGetPresentCoreCount();
1126 return S_OK;
1127}
1128
1129/**
1130 * Returns the number of installed physical processor cores.
1131 *
1132 * @returns COM status code
1133 * @param aCount address of result variable
1134 */
1135HRESULT Host::getProcessorOnlineCoreCount(ULONG *aCount)
1136{
1137 // no locking required
1138
1139 *aCount = RTMpGetOnlineCoreCount();
1140 return S_OK;
1141}
1142
1143/**
1144 * Returns the (approximate) maximum speed of the given host CPU in MHz
1145 *
1146 * @returns COM status code
1147 * @param aCpuId id to get info for.
1148 * @param aSpeed address of result variable, speed is 0 if unknown or aCpuId
1149 * is invalid.
1150 */
1151HRESULT Host::getProcessorSpeed(ULONG aCpuId,
1152 ULONG *aSpeed)
1153{
1154 // no locking required
1155
1156 *aSpeed = RTMpGetMaxFrequency(aCpuId);
1157 return S_OK;
1158}
1159
1160/**
1161 * Returns a description string for the host CPU
1162 *
1163 * @returns COM status code
1164 * @param aCpuId id to get info for.
1165 * @param aDescription address of result variable, empty string if not known
1166 * or aCpuId is invalid.
1167 */
1168HRESULT Host::getProcessorDescription(ULONG aCpuId, com::Utf8Str &aDescription)
1169{
1170 // no locking required
1171
1172 int vrc = aDescription.reserveNoThrow(80);
1173 if (RT_SUCCESS(vrc))
1174 {
1175 vrc = RTMpGetDescription(aCpuId, aDescription.mutableRaw(), aDescription.capacity());
1176 if (RT_SUCCESS(vrc))
1177 {
1178 aDescription.jolt();
1179 return S_OK;
1180 }
1181 }
1182 return setErrorVrc(vrc);
1183}
1184
1185/**
1186 * Updates fVTSupported, fNestedPagingSupported, fUnrestrictedGuestSupported,
1187 * fVirtVmsaveVmload and fNestedHWVirtSupported with info from SUPR3QueryVTCaps().
1188 *
1189 * This is repeated till we successfully open the support driver, in case it
1190 * is loaded after VBoxSVC starts.
1191 */
1192void Host::i_updateProcessorFeatures()
1193{
1194 /* Perhaps the driver is available now... */
1195 int rc = SUPR3InitEx(SUPR3INIT_F_LIMITED, NULL);
1196 if (RT_SUCCESS(rc))
1197 {
1198 uint32_t fVTCaps;
1199 rc = SUPR3QueryVTCaps(&fVTCaps);
1200 AssertMsg(RT_SUCCESS(rc) || rc == VERR_SUP_DRIVERLESS, ("SUPR3QueryVTCaps failed rc=%Rrc\n", rc));
1201
1202 SUPR3Term(false);
1203
1204 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
1205 if (RT_FAILURE(rc))
1206 {
1207 fVTCaps = 0;
1208 if (rc != VERR_SUP_DRIVERLESS)
1209 LogRel(("SUPR0QueryVTCaps -> %Rrc\n", rc));
1210# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) /* Preserve detected VT-x/AMD-V support for show. */
1211 else
1212 fVTCaps = m->fVTSupported ? SUPVTCAPS_AMD_V | SUPVTCAPS_VT_X : 0;
1213# endif
1214 }
1215 m->fVTSupported = (fVTCaps & (SUPVTCAPS_AMD_V | SUPVTCAPS_VT_X)) != 0;
1216 m->fNestedPagingSupported = (fVTCaps & SUPVTCAPS_NESTED_PAGING) != 0;
1217 m->fUnrestrictedGuestSupported = (fVTCaps & (SUPVTCAPS_AMD_V | SUPVTCAPS_VTX_UNRESTRICTED_GUEST)) != 0;
1218 m->fNestedHWVirtSupported = (fVTCaps & (SUPVTCAPS_AMD_V | SUPVTCAPS_NESTED_PAGING))
1219 == (SUPVTCAPS_AMD_V | SUPVTCAPS_NESTED_PAGING)
1220 || (fVTCaps & ( SUPVTCAPS_VT_X | SUPVTCAPS_NESTED_PAGING
1221 | SUPVTCAPS_VTX_UNRESTRICTED_GUEST | SUPVTCAPS_VTX_VMCS_SHADOWING))
1222 == ( SUPVTCAPS_VT_X | SUPVTCAPS_NESTED_PAGING
1223 | SUPVTCAPS_VTX_UNRESTRICTED_GUEST | SUPVTCAPS_VTX_VMCS_SHADOWING);
1224 m->fVirtVmsaveVmload = (fVTCaps & SUPVTCAPS_AMDV_VIRT_VMSAVE_VMLOAD) != 0;
1225 m->fRecheckVTSupported = false; /* No need to try again, we cached everything. */
1226 }
1227}
1228
1229/**
1230 * Returns whether a host processor feature is supported or not
1231 *
1232 * @returns COM status code
1233 * @param aFeature to query.
1234 * @param aSupported supported bool result variable
1235 */
1236HRESULT Host::getProcessorFeature(ProcessorFeature_T aFeature, BOOL *aSupported)
1237{
1238 /* Validate input. */
1239 switch (aFeature)
1240 {
1241 case ProcessorFeature_HWVirtEx:
1242 case ProcessorFeature_PAE:
1243 case ProcessorFeature_LongMode:
1244 case ProcessorFeature_NestedPaging:
1245 case ProcessorFeature_UnrestrictedGuest:
1246 case ProcessorFeature_NestedHWVirt:
1247 case ProcessorFeature_VirtVmsaveVmload:
1248 break;
1249 default:
1250 return setError(E_INVALIDARG, tr("The aFeature value %d (%#x) is out of range."), (int)aFeature, (int)aFeature);
1251 }
1252
1253 /* Do the job. */
1254 AutoCaller autoCaller(this);
1255 HRESULT hrc = autoCaller.rc();
1256 if (SUCCEEDED(hrc))
1257 {
1258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1259
1260 if ( m->fRecheckVTSupported
1261 && ( aFeature == ProcessorFeature_HWVirtEx
1262 || aFeature == ProcessorFeature_NestedPaging
1263 || aFeature == ProcessorFeature_UnrestrictedGuest
1264 || aFeature == ProcessorFeature_NestedHWVirt
1265 || aFeature == ProcessorFeature_VirtVmsaveVmload)
1266 )
1267 {
1268 alock.release();
1269 i_updateProcessorFeatures();
1270 alock.acquire();
1271 }
1272
1273 switch (aFeature)
1274 {
1275 case ProcessorFeature_HWVirtEx:
1276 *aSupported = m->fVTSupported;
1277 break;
1278
1279 case ProcessorFeature_PAE:
1280 *aSupported = m->fPAESupported;
1281 break;
1282
1283 case ProcessorFeature_LongMode:
1284 *aSupported = m->fLongModeSupported;
1285 break;
1286
1287 case ProcessorFeature_NestedPaging:
1288 *aSupported = m->fNestedPagingSupported;
1289 break;
1290
1291 case ProcessorFeature_UnrestrictedGuest:
1292 *aSupported = m->fUnrestrictedGuestSupported;
1293 break;
1294
1295 case ProcessorFeature_NestedHWVirt:
1296 *aSupported = m->fNestedHWVirtSupported;
1297 break;
1298
1299 case ProcessorFeature_VirtVmsaveVmload:
1300 *aSupported = m->fVirtVmsaveVmload;
1301 break;
1302
1303 default:
1304 AssertFailed();
1305 }
1306 }
1307 return hrc;
1308}
1309
1310/**
1311 * Returns the specific CPUID leaf.
1312 *
1313 * @returns COM status code
1314 * @param aCpuId The CPU number. Mostly ignored.
1315 * @param aLeaf The leaf number.
1316 * @param aSubLeaf The sub-leaf number.
1317 * @param aValEAX Where to return EAX.
1318 * @param aValEBX Where to return EBX.
1319 * @param aValECX Where to return ECX.
1320 * @param aValEDX Where to return EDX.
1321 */
1322HRESULT Host::getProcessorCPUIDLeaf(ULONG aCpuId, ULONG aLeaf, ULONG aSubLeaf,
1323 ULONG *aValEAX, ULONG *aValEBX, ULONG *aValECX, ULONG *aValEDX)
1324{
1325 // no locking required
1326
1327 /* Check that the CPU is online. */
1328 /** @todo later use RTMpOnSpecific. */
1329 if (!RTMpIsCpuOnline(aCpuId))
1330 return RTMpIsCpuPresent(aCpuId)
1331 ? setError(E_FAIL, tr("CPU no.%u is not present"), aCpuId)
1332 : setError(E_FAIL, tr("CPU no.%u is not online"), aCpuId);
1333
1334#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
1335 uint32_t uEAX, uEBX, uECX, uEDX;
1336 ASMCpuId_Idx_ECX(aLeaf, aSubLeaf, &uEAX, &uEBX, &uECX, &uEDX);
1337 *aValEAX = uEAX;
1338 *aValEBX = uEBX;
1339 *aValECX = uECX;
1340 *aValEDX = uEDX;
1341#else
1342 *aValEAX = 0;
1343 *aValEBX = 0;
1344 *aValECX = 0;
1345 *aValEDX = 0;
1346#endif
1347
1348 return S_OK;
1349}
1350
1351/**
1352 * Returns the amount of installed system memory in megabytes
1353 *
1354 * @returns COM status code
1355 * @param aSize address of result variable
1356 */
1357HRESULT Host::getMemorySize(ULONG *aSize)
1358{
1359 // no locking required
1360
1361 uint64_t cb;
1362 int rc = RTSystemQueryTotalRam(&cb);
1363 if (RT_FAILURE(rc))
1364 return E_FAIL;
1365 *aSize = (ULONG)(cb / _1M);
1366 return S_OK;
1367}
1368
1369/**
1370 * Returns the current system memory free space in megabytes
1371 *
1372 * @returns COM status code
1373 * @param aAvailable address of result variable
1374 */
1375HRESULT Host::getMemoryAvailable(ULONG *aAvailable)
1376{
1377 // no locking required
1378
1379 uint64_t cb;
1380 int rc = RTSystemQueryAvailableRam(&cb);
1381 if (RT_FAILURE(rc))
1382 return E_FAIL;
1383 *aAvailable = (ULONG)(cb / _1M);
1384 return S_OK;
1385}
1386
1387/**
1388 * Returns the name string of the host operating system
1389 *
1390 * @returns COM status code
1391 * @param aOperatingSystem result variable
1392 */
1393HRESULT Host::getOperatingSystem(com::Utf8Str &aOperatingSystem)
1394{
1395 // no locking required
1396
1397 char szOSName[80];
1398 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szOSName, sizeof(szOSName));
1399 if (RT_FAILURE(vrc))
1400 return E_FAIL; /** @todo error reporting? */
1401 aOperatingSystem = Utf8Str(szOSName);
1402 return S_OK;
1403}
1404
1405/**
1406 * Returns the version string of the host operating system
1407 *
1408 * @returns COM status code
1409 * @param aVersion address of result variable
1410 */
1411HRESULT Host::getOSVersion(com::Utf8Str &aVersion)
1412{
1413 // no locking required
1414
1415 /* Get the OS release. Reserve some buffer space for the service pack. */
1416 char szOSRelease[128];
1417 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOSRelease, sizeof(szOSRelease) - 32);
1418 if (RT_FAILURE(vrc))
1419 return E_FAIL; /** @todo error reporting? */
1420
1421 /* Append the service pack if present. */
1422 char szOSServicePack[80];
1423 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szOSServicePack, sizeof(szOSServicePack));
1424 if (RT_FAILURE(vrc))
1425 {
1426 if (vrc != VERR_NOT_SUPPORTED)
1427 return E_FAIL; /** @todo error reporting? */
1428 szOSServicePack[0] = '\0';
1429 }
1430 if (szOSServicePack[0] != '\0')
1431 {
1432 char *psz = strchr(szOSRelease, '\0');
1433 RTStrPrintf(psz, (size_t)(&szOSRelease[sizeof(szOSRelease)] - psz), "sp%s", szOSServicePack);
1434 }
1435
1436 aVersion = szOSRelease;
1437 return S_OK;
1438}
1439
1440/**
1441 * Returns the current host time in milliseconds since 1970-01-01 UTC.
1442 *
1443 * @returns COM status code
1444 * @param aUTCTime address of result variable
1445 */
1446HRESULT Host::getUTCTime(LONG64 *aUTCTime)
1447{
1448 // no locking required
1449
1450 RTTIMESPEC now;
1451 *aUTCTime = RTTimeSpecGetMilli(RTTimeNow(&now));
1452
1453 return S_OK;
1454}
1455
1456
1457HRESULT Host::getAcceleration3DAvailable(BOOL *aSupported)
1458{
1459 HRESULT hrc = S_OK;
1460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1461 if (m->f3DAccelerationSupported != -1)
1462 *aSupported = m->f3DAccelerationSupported;
1463 else
1464 {
1465 alock.release();
1466
1467#ifdef VBOX_WITH_3D_ACCELERATION
1468 bool fSupported = VBoxOglIs3DAccelerationSupported();
1469#else
1470 bool fSupported = false; /* shouldn't get here, but just in case. */
1471#endif
1472 AutoWriteLock alock2(this COMMA_LOCKVAL_SRC_POS);
1473
1474 m->f3DAccelerationSupported = fSupported;
1475 alock2.release();
1476 *aSupported = fSupported;
1477 }
1478
1479#ifdef DEBUG_misha
1480 AssertMsgFailed(("should not be here any more!\n"));
1481#endif
1482
1483 return hrc;
1484}
1485
1486HRESULT Host::createHostOnlyNetworkInterface(ComPtr<IHostNetworkInterface> &aHostInterface,
1487 ComPtr<IProgress> &aProgress)
1488
1489{
1490#ifdef VBOX_WITH_HOSTNETIF_API
1491 /* No need to lock anything. If there ever will - watch out, the function
1492 * called below grabs the VirtualBox lock. */
1493
1494 int vrc = NetIfCreateHostOnlyNetworkInterface(m->pParent, aHostInterface.asOutParam(), aProgress.asOutParam());
1495 if (RT_SUCCESS(vrc))
1496 {
1497 if (aHostInterface.isNull())
1498 return setError(E_FAIL,
1499 tr("Unable to create a host network interface"));
1500
1501# if !defined(RT_OS_WINDOWS)
1502 Bstr tmpAddr, tmpMask, tmpName;
1503 HRESULT hrc;
1504 hrc = aHostInterface->COMGETTER(Name)(tmpName.asOutParam());
1505 ComAssertComRCRet(hrc, hrc);
1506 hrc = aHostInterface->COMGETTER(IPAddress)(tmpAddr.asOutParam());
1507 ComAssertComRCRet(hrc, hrc);
1508 hrc = aHostInterface->COMGETTER(NetworkMask)(tmpMask.asOutParam());
1509 ComAssertComRCRet(hrc, hrc);
1510
1511 /*
1512 * We need to write the default IP address and mask to extra data now,
1513 * so the interface gets re-created after vboxnetadp.ko reload.
1514 * Note that we avoid calling EnableStaticIpConfig since it would
1515 * change the address on host's interface as well and we want to
1516 * postpone the change until VM actually starts.
1517 */
1518 hrc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPAddress", tmpName.raw()).raw(),
1519 tmpAddr.raw());
1520 ComAssertComRCRet(hrc, hrc);
1521
1522 hrc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPNetMask", tmpName.raw()).raw(),
1523 tmpMask.raw());
1524 ComAssertComRCRet(hrc, hrc);
1525# endif /* !defined(RT_OS_WINDOWS) */
1526 }
1527
1528 return S_OK;
1529#else
1530 RT_NOREF(aHostInterface, aProgress);
1531 return E_NOTIMPL;
1532#endif
1533}
1534
1535
1536#ifdef RT_OS_WINDOWS
1537HRESULT Host::i_removePersistentConfig(const Bstr &bstrGuid)
1538{
1539 HRESULT hrc = m->pParent->SetExtraData(BstrFmt("HostOnly/{%ls}/Name", bstrGuid.raw()).raw(), NULL);
1540 if (SUCCEEDED(hrc)) hrc = m->pParent->SetExtraData(BstrFmt("HostOnly/{%ls}/IPAddress", bstrGuid.raw()).raw(), NULL);
1541 if (SUCCEEDED(hrc)) hrc = m->pParent->SetExtraData(BstrFmt("HostOnly/{%ls}/IPNetMask", bstrGuid.raw()).raw(), NULL);
1542 if (SUCCEEDED(hrc)) hrc = m->pParent->SetExtraData(BstrFmt("HostOnly/{%ls}/IPV6Address", bstrGuid.raw()).raw(), NULL);
1543 if (SUCCEEDED(hrc)) hrc = m->pParent->SetExtraData(BstrFmt("HostOnly/{%ls}/IPV6PrefixLen", bstrGuid.raw()).raw(), NULL);
1544 return hrc;
1545}
1546#endif /* RT_OS_WINDOWS */
1547
1548HRESULT Host::removeHostOnlyNetworkInterface(const com::Guid &aId,
1549 ComPtr<IProgress> &aProgress)
1550
1551{
1552#ifdef VBOX_WITH_HOSTNETIF_API
1553 /* No need to lock anything, the code below does not touch the state
1554 * of the host object. If that ever changes then check for lock order
1555 * violations with the called functions. */
1556
1557 Bstr name;
1558 HRESULT rc;
1559
1560 /* first check whether an interface with the given name already exists */
1561 {
1562 ComPtr<IHostNetworkInterface> iface;
1563 rc = findHostNetworkInterfaceById(aId, iface);
1564 if (FAILED(rc))
1565 return setError(VBOX_E_OBJECT_NOT_FOUND,
1566 tr("Host network interface with UUID {%RTuuid} does not exist"),
1567 Guid(aId).raw());
1568 rc = iface->COMGETTER(Name)(name.asOutParam());
1569 ComAssertComRCRet(rc, rc);
1570 }
1571
1572 int r = NetIfRemoveHostOnlyNetworkInterface(m->pParent, aId, aProgress.asOutParam());
1573 if (RT_SUCCESS(r))
1574 {
1575 /* Drop configuration parameters for removed interface */
1576#ifdef RT_OS_WINDOWS
1577 rc = i_removePersistentConfig(Utf8StrFmt("%RTuuid", &aId));
1578 if (FAILED(rc))
1579 LogRel(("i_removePersistentConfig(%RTuuid) failed with 0x%x\n", &aId, rc));
1580#else /* !RT_OS_WINDOWS */
1581 rc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPAddress", name.raw()).raw(), NULL);
1582 rc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPNetMask", name.raw()).raw(), NULL);
1583 rc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPV6Address", name.raw()).raw(), NULL);
1584 rc = m->pParent->SetExtraData(BstrFmt("HostOnly/%ls/IPV6NetMask", name.raw()).raw(), NULL);
1585#endif /* !RT_OS_WINDOWS */
1586
1587 return S_OK;
1588 }
1589
1590 return r == VERR_NOT_IMPLEMENTED ? E_NOTIMPL : E_FAIL;
1591#else
1592 RT_NOREF(aId, aProgress);
1593 return E_NOTIMPL;
1594#endif
1595}
1596
1597HRESULT Host::createUSBDeviceFilter(const com::Utf8Str &aName,
1598 ComPtr<IHostUSBDeviceFilter> &aFilter)
1599{
1600#ifdef VBOX_WITH_USB
1601
1602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1603
1604 ComObjPtr<HostUSBDeviceFilter> filter;
1605 filter.createObject();
1606 HRESULT rc = filter->init(this, Bstr(aName).raw());
1607 ComAssertComRCRet(rc, rc);
1608 rc = filter.queryInterfaceTo(aFilter.asOutParam());
1609 AssertComRCReturn(rc, rc);
1610 return S_OK;
1611#else
1612 /* Note: The GUI depends on this method returning E_NOTIMPL with no
1613 * extended error info to indicate that USB is simply not available
1614 * (w/o treating it as a failure), for example, as in OSE. */
1615 RT_NOREF(aName, aFilter);
1616 ReturnComNotImplemented();
1617#endif
1618}
1619
1620HRESULT Host::insertUSBDeviceFilter(ULONG aPosition,
1621 const ComPtr<IHostUSBDeviceFilter> &aFilter)
1622{
1623#ifdef VBOX_WITH_USB
1624 /* Note: HostUSBDeviceFilter and USBProxyService also uses this lock. */
1625
1626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1627
1628 MultiResult rc = i_checkUSBProxyService();
1629 if (FAILED(rc))
1630 return rc;
1631
1632 ComObjPtr<HostUSBDeviceFilter> pFilter;
1633 for (USBDeviceFilterList::iterator it = m->llChildren.begin();
1634 it != m->llChildren.end();
1635 ++it)
1636 {
1637 if (*it == aFilter)
1638 {
1639 pFilter = *it;
1640 break;
1641 }
1642 }
1643 if (pFilter.isNull())
1644 return setError(VBOX_E_INVALID_OBJECT_STATE,
1645 tr("The given USB device filter is not created within this VirtualBox instance"));
1646
1647 if (pFilter->mInList)
1648 return setError(E_INVALIDARG,
1649 tr("The given USB device filter is already in the list"));
1650
1651 /* iterate to the position... */
1652 USBDeviceFilterList::iterator itPos = m->llUSBDeviceFilters.begin();
1653 std::advance(itPos, aPosition);
1654 /* ...and insert */
1655 m->llUSBDeviceFilters.insert(itPos, pFilter);
1656 pFilter->mInList = true;
1657
1658 /* notify the proxy (only when the filter is active) */
1659 if ( m->pUSBProxyService->isActive()
1660 && pFilter->i_getData().mData.fActive)
1661 {
1662 ComAssertRet(pFilter->i_getId() == NULL, E_FAIL);
1663 pFilter->i_getId() = m->pUSBProxyService->insertFilter(&pFilter->i_getData().mUSBFilter);
1664 }
1665
1666 // save the global settings; for that we should hold only the VirtualBox lock
1667 alock.release();
1668 AutoWriteLock vboxLock(m->pParent COMMA_LOCKVAL_SRC_POS);
1669 return rc = m->pParent->i_saveSettings();
1670#else
1671
1672 /* Note: The GUI depends on this method returning E_NOTIMPL with no
1673 * extended error info to indicate that USB is simply not available
1674 * (w/o treating it as a failure), for example, as in OSE. */
1675 RT_NOREF(aPosition, aFilter);
1676 ReturnComNotImplemented();
1677#endif
1678}
1679
1680HRESULT Host::removeUSBDeviceFilter(ULONG aPosition)
1681{
1682#ifdef VBOX_WITH_USB
1683
1684 /* Note: HostUSBDeviceFilter and USBProxyService also uses this lock. */
1685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 MultiResult rc = i_checkUSBProxyService();
1688 if (FAILED(rc))
1689 return rc;
1690
1691 if (!m->llUSBDeviceFilters.size())
1692 return setError(E_INVALIDARG,
1693 tr("The USB device filter list is empty"));
1694
1695 if (aPosition >= m->llUSBDeviceFilters.size())
1696 return setError(E_INVALIDARG,
1697 tr("Invalid position: %lu (must be in range [0, %lu])"),
1698 aPosition, m->llUSBDeviceFilters.size() - 1);
1699
1700 ComObjPtr<HostUSBDeviceFilter> filter;
1701 {
1702 /* iterate to the position... */
1703 USBDeviceFilterList::iterator it = m->llUSBDeviceFilters.begin();
1704 std::advance(it, aPosition);
1705 /* ...get an element from there... */
1706 filter = *it;
1707 /* ...and remove */
1708 filter->mInList = false;
1709 m->llUSBDeviceFilters.erase(it);
1710 }
1711
1712 /* notify the proxy (only when the filter is active) */
1713 if (m->pUSBProxyService->isActive() && filter->i_getData().mData.fActive)
1714 {
1715 ComAssertRet(filter->i_getId() != NULL, E_FAIL);
1716 m->pUSBProxyService->removeFilter(filter->i_getId());
1717 filter->i_getId() = NULL;
1718 }
1719
1720 // save the global settings; for that we should hold only the VirtualBox lock
1721 alock.release();
1722 AutoWriteLock vboxLock(m->pParent COMMA_LOCKVAL_SRC_POS);
1723 return rc = m->pParent->i_saveSettings();
1724#else
1725 /* Note: The GUI depends on this method returning E_NOTIMPL with no
1726 * extended error info to indicate that USB is simply not available
1727 * (w/o treating it as a failure), for example, as in OSE. */
1728 RT_NOREF(aPosition);
1729 ReturnComNotImplemented();
1730#endif
1731}
1732
1733HRESULT Host::findHostDVDDrive(const com::Utf8Str &aName,
1734 ComPtr<IMedium> &aDrive)
1735{
1736 ComObjPtr<Medium> medium;
1737 HRESULT rc = i_findHostDriveByNameOrId(DeviceType_DVD, aName, medium);
1738 if (SUCCEEDED(rc))
1739 rc = medium.queryInterfaceTo(aDrive.asOutParam());
1740 else
1741 rc = setError(rc, tr("The host DVD drive named '%s' could not be found"), aName.c_str());
1742 return rc;
1743}
1744
1745HRESULT Host::findHostFloppyDrive(const com::Utf8Str &aName, ComPtr<IMedium> &aDrive)
1746{
1747 aDrive = NULL;
1748
1749 ComObjPtr<Medium>medium;
1750
1751 HRESULT rc = i_findHostDriveByNameOrId(DeviceType_Floppy, aName, medium);
1752 if (SUCCEEDED(rc))
1753 return medium.queryInterfaceTo(aDrive.asOutParam());
1754 else
1755 return setError(rc, tr("The host floppy drive named '%s' could not be found"), aName.c_str());
1756}
1757
1758HRESULT Host::findHostNetworkInterfaceByName(const com::Utf8Str &aName,
1759 ComPtr<IHostNetworkInterface> &aNetworkInterface)
1760{
1761#ifndef VBOX_WITH_HOSTNETIF_API
1762 RT_NOREF(aName, aNetworkInterface);
1763 return E_NOTIMPL;
1764#else
1765 if (!aName.length())
1766 return E_INVALIDARG;
1767
1768 HRESULT rc = i_updateNetIfList();
1769 if (FAILED(rc))
1770 {
1771 Log(("Failed to update host network interface list with rc=%Rhrc\n", rc));
1772 return rc;
1773 }
1774#if defined(RT_OS_WINDOWS)
1775 rc = i_updatePersistentConfigForHostOnlyAdapters();
1776 if (FAILED(rc))
1777 {
1778 LogRel(("Failed to update persistent config for host-only adapters with rc=%Rhrc\n", rc));
1779 return rc;
1780 }
1781#endif /* defined(RT_OS_WINDOWS) */
1782
1783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1784
1785 ComObjPtr<HostNetworkInterface> found;
1786 for (HostNetworkInterfaceList::iterator it = m->llNetIfs.begin(); it != m->llNetIfs.end(); ++it)
1787 {
1788 Bstr n;
1789 (*it)->COMGETTER(Name)(n.asOutParam());
1790 if (n == aName)
1791 found = *it;
1792 }
1793
1794 if (!found)
1795 return setError(E_INVALIDARG,
1796 tr("The host network interface named '%s' could not be found"), aName.c_str());
1797
1798 return found.queryInterfaceTo(aNetworkInterface.asOutParam());
1799#endif
1800}
1801
1802HRESULT Host::findHostNetworkInterfaceById(const com::Guid &aId,
1803 ComPtr<IHostNetworkInterface> &aNetworkInterface)
1804{
1805#ifndef VBOX_WITH_HOSTNETIF_API
1806 RT_NOREF(aId, aNetworkInterface);
1807 return E_NOTIMPL;
1808#else
1809 if (!aId.isValid())
1810 return E_INVALIDARG;
1811
1812 HRESULT rc = i_updateNetIfList();
1813 if (FAILED(rc))
1814 {
1815 Log(("Failed to update host network interface list with rc=%Rhrc\n", rc));
1816 return rc;
1817 }
1818#if defined(RT_OS_WINDOWS)
1819 rc = i_updatePersistentConfigForHostOnlyAdapters();
1820 if (FAILED(rc))
1821 {
1822 LogRel(("Failed to update persistent config for host-only adapters with rc=%Rhrc\n", rc));
1823 return rc;
1824 }
1825#endif /* defined(RT_OS_WINDOWS) */
1826
1827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1828
1829 ComObjPtr<HostNetworkInterface> found;
1830 for (HostNetworkInterfaceList::iterator it = m->llNetIfs.begin(); it != m->llNetIfs.end(); ++it)
1831 {
1832 Bstr g;
1833 (*it)->COMGETTER(Id)(g.asOutParam());
1834 if (Guid(g) == aId)
1835 found = *it;
1836 }
1837
1838 if (!found)
1839 return setError(E_INVALIDARG,
1840 tr("The host network interface with the given GUID could not be found"));
1841 return found.queryInterfaceTo(aNetworkInterface.asOutParam());
1842
1843#endif
1844}
1845
1846HRESULT Host::findHostNetworkInterfacesOfType(HostNetworkInterfaceType_T aType,
1847 std::vector<ComPtr<IHostNetworkInterface> > &aNetworkInterfaces)
1848{
1849#ifdef VBOX_WITH_HOSTNETIF_API
1850 HRESULT rc = i_updateNetIfList();
1851 if (FAILED(rc))
1852 {
1853 Log(("Failed to update host network interface list with rc=%Rhrc\n", rc));
1854 return rc;
1855 }
1856#if defined(RT_OS_WINDOWS)
1857 rc = i_updatePersistentConfigForHostOnlyAdapters();
1858 if (FAILED(rc))
1859 {
1860 LogRel(("Failed to update persistent config for host-only adapters with rc=%Rhrc\n", rc));
1861 return rc;
1862 }
1863#endif /* defined(RT_OS_WINDOWS) */
1864
1865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1866
1867 HostNetworkInterfaceList resultList;
1868 for (HostNetworkInterfaceList::iterator it = m->llNetIfs.begin(); it != m->llNetIfs.end(); ++it)
1869 {
1870 HostNetworkInterfaceType_T t;
1871 HRESULT hr = (*it)->COMGETTER(InterfaceType)(&t);
1872 if (FAILED(hr))
1873 return hr;
1874
1875 if (t == aType)
1876 resultList.push_back(*it);
1877 }
1878 aNetworkInterfaces.resize(resultList.size());
1879 size_t i = 0;
1880 for (HostNetworkInterfaceList::iterator it = resultList.begin(); it != resultList.end(); ++it, ++i)
1881 {
1882 (*it).queryInterfaceTo(aNetworkInterfaces[i].asOutParam());
1883 }
1884
1885 return S_OK;
1886#else
1887 RT_NOREF(aType, aNetworkInterfaces);
1888 return E_NOTIMPL;
1889#endif
1890}
1891
1892HRESULT Host::findUSBDeviceByAddress(const com::Utf8Str &aName,
1893 ComPtr<IHostUSBDevice> &aDevice)
1894{
1895#ifdef VBOX_WITH_USB
1896
1897 aDevice = NULL;
1898 SafeIfaceArray<IHostUSBDevice> devsvec;
1899 HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
1900 if (FAILED(rc))
1901 return rc;
1902
1903 for (size_t i = 0; i < devsvec.size(); ++i)
1904 {
1905 Bstr address;
1906 rc = devsvec[i]->COMGETTER(Address)(address.asOutParam());
1907 if (FAILED(rc))
1908 return rc;
1909 if (address == aName)
1910 {
1911 return (ComPtr<IHostUSBDevice>(devsvec[i]).queryInterfaceTo(aDevice.asOutParam()));
1912 }
1913 }
1914
1915 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
1916 tr("Could not find a USB device with address '%s'"),
1917 aName.c_str());
1918
1919#else /* !VBOX_WITH_USB */
1920 RT_NOREF(aName, aDevice);
1921 return E_NOTIMPL;
1922#endif /* !VBOX_WITH_USB */
1923}
1924HRESULT Host::findUSBDeviceById(const com::Guid &aId,
1925 ComPtr<IHostUSBDevice> &aDevice)
1926{
1927#ifdef VBOX_WITH_USB
1928 if (!aId.isValid())
1929 return E_INVALIDARG;
1930
1931 aDevice = NULL;
1932
1933 SafeIfaceArray<IHostUSBDevice> devsvec;
1934 HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
1935 if (FAILED(rc))
1936 return rc;
1937
1938 for (size_t i = 0; i < devsvec.size(); ++i)
1939 {
1940 Bstr id;
1941 rc = devsvec[i]->COMGETTER(Id)(id.asOutParam());
1942 if (FAILED(rc))
1943 return rc;
1944 if (Guid(id) == aId)
1945 {
1946 return (ComPtr<IHostUSBDevice>(devsvec[i]).queryInterfaceTo(aDevice.asOutParam()));
1947 }
1948 }
1949 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
1950 tr("Could not find a USB device with uuid {%RTuuid}"),
1951 aId.raw());
1952
1953#else /* !VBOX_WITH_USB */
1954 RT_NOREF(aId, aDevice);
1955 return E_NOTIMPL;
1956#endif /* !VBOX_WITH_USB */
1957}
1958
1959HRESULT Host::generateMACAddress(com::Utf8Str &aAddress)
1960{
1961 // no locking required
1962 i_generateMACAddress(aAddress);
1963 return S_OK;
1964}
1965
1966/**
1967 * Returns a list of host video capture devices (webcams, etc).
1968 *
1969 * @returns COM status code
1970 * @param aVideoInputDevices Array of interface pointers to be filled.
1971 */
1972HRESULT Host::getVideoInputDevices(std::vector<ComPtr<IHostVideoInputDevice> > &aVideoInputDevices)
1973{
1974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1975 HostVideoInputDeviceList list;
1976
1977 HRESULT rc = HostVideoInputDevice::queryHostDevices(m->pParent, &list);
1978 if (FAILED(rc))
1979 return rc;
1980
1981 aVideoInputDevices.resize(list.size());
1982 size_t i = 0;
1983 for (HostVideoInputDeviceList::const_iterator it = list.begin(); it != list.end(); ++it, ++i)
1984 (*it).queryInterfaceTo(aVideoInputDevices[i].asOutParam());
1985
1986 return S_OK;
1987}
1988
1989HRESULT Host::addUSBDeviceSource(const com::Utf8Str &aBackend, const com::Utf8Str &aId, const com::Utf8Str &aAddress,
1990 const std::vector<com::Utf8Str> &aPropertyNames, const std::vector<com::Utf8Str> &aPropertyValues)
1991{
1992#ifdef VBOX_WITH_USB
1993 /* The USB proxy service will do the locking. */
1994 return m->pUSBProxyService->addUSBDeviceSource(aBackend, aId, aAddress, aPropertyNames, aPropertyValues);
1995#else
1996 RT_NOREF(aBackend, aId, aAddress, aPropertyNames, aPropertyValues);
1997 ReturnComNotImplemented();
1998#endif
1999}
2000
2001HRESULT Host::removeUSBDeviceSource(const com::Utf8Str &aId)
2002{
2003#ifdef VBOX_WITH_USB
2004 /* The USB proxy service will do the locking. */
2005 return m->pUSBProxyService->removeUSBDeviceSource(aId);
2006#else
2007 RT_NOREF(aId);
2008 ReturnComNotImplemented();
2009#endif
2010}
2011
2012HRESULT Host::getUpdateHost(ComPtr<IUpdateAgent> &aUpdate)
2013{
2014#ifdef VBOX_WITH_UPDATE_AGENT
2015 HRESULT hrc = m->pUpdateHost.queryInterfaceTo(aUpdate.asOutParam());
2016 return hrc;
2017#else
2018 RT_NOREF(aUpdate);
2019 ReturnComNotImplemented();
2020#endif
2021}
2022
2023HRESULT Host::getUpdateExtPack(ComPtr<IUpdateAgent> &aUpdate)
2024{
2025 RT_NOREF(aUpdate);
2026 ReturnComNotImplemented();
2027}
2028
2029HRESULT Host::getUpdateGuestAdditions(ComPtr<IUpdateAgent> &aUpdate)
2030{
2031 RT_NOREF(aUpdate);
2032 ReturnComNotImplemented();
2033}
2034
2035HRESULT Host::getHostDrives(std::vector<ComPtr<IHostDrive> > &aHostDrives)
2036{
2037 std::list<std::pair<com::Utf8Str, com::Utf8Str> > llDrivesPathsList;
2038 HRESULT hrc = i_getDrivesPathsList(llDrivesPathsList);
2039 if (SUCCEEDED(hrc))
2040 {
2041 for (std::list<std::pair<com::Utf8Str, com::Utf8Str> >::const_iterator it = llDrivesPathsList.begin();
2042 it != llDrivesPathsList.end();
2043 ++it)
2044 {
2045 ComObjPtr<HostDrive> pHostDrive;
2046 hrc = pHostDrive.createObject();
2047 if (SUCCEEDED(hrc))
2048 hrc = pHostDrive->initFromPathAndModel(it->first, it->second);
2049 if (FAILED(hrc))
2050 break;
2051 aHostDrives.push_back(pHostDrive);
2052 }
2053 }
2054 return hrc;
2055}
2056
2057
2058// public methods only for internal purposes
2059////////////////////////////////////////////////////////////////////////////////
2060
2061HRESULT Host::i_loadSettings(const settings::Host &data)
2062{
2063 HRESULT rc = S_OK;
2064#ifdef VBOX_WITH_USB
2065 AutoCaller autoCaller(this);
2066 if (FAILED(autoCaller.rc()))
2067 return autoCaller.rc();
2068
2069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2070
2071 for (settings::USBDeviceFiltersList::const_iterator it = data.llUSBDeviceFilters.begin();
2072 it != data.llUSBDeviceFilters.end();
2073 ++it)
2074 {
2075 const settings::USBDeviceFilter &f = *it;
2076 ComObjPtr<HostUSBDeviceFilter> pFilter;
2077 pFilter.createObject();
2078 rc = pFilter->init(this, f);
2079 if (FAILED(rc))
2080 break;
2081
2082 m->llUSBDeviceFilters.push_back(pFilter);
2083 pFilter->mInList = true;
2084
2085 /* notify the proxy (only when the filter is active) */
2086 if (pFilter->i_getData().mData.fActive)
2087 {
2088 HostUSBDeviceFilter *flt = pFilter; /* resolve ambiguity */
2089 flt->i_getId() = m->pUSBProxyService->insertFilter(&pFilter->i_getData().mUSBFilter);
2090 }
2091 }
2092
2093 rc = m->pUSBProxyService->i_loadSettings(data.llUSBDeviceSources);
2094#else
2095 RT_NOREF(data);
2096#endif /* VBOX_WITH_USB */
2097
2098#ifdef VBOX_WITH_UPDATE_AGENT
2099 rc = m->pUpdateHost->i_loadSettings(data.updateHost);
2100 ComAssertComRCRet(rc, rc);
2101 /** @todo Add handling for ExtPack and Guest Additions updates here later. See @bugref{7983}. */
2102#endif
2103
2104 return rc;
2105}
2106
2107HRESULT Host::i_saveSettings(settings::Host &data)
2108{
2109 AutoCaller autoCaller(this);
2110 if (FAILED(autoCaller.rc()))
2111 return autoCaller.rc();
2112
2113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 HRESULT rc;
2116
2117#ifdef VBOX_WITH_USB
2118 data.llUSBDeviceFilters.clear();
2119 data.llUSBDeviceSources.clear();
2120
2121 for (USBDeviceFilterList::const_iterator it = m->llUSBDeviceFilters.begin();
2122 it != m->llUSBDeviceFilters.end();
2123 ++it)
2124 {
2125 ComObjPtr<HostUSBDeviceFilter> pFilter = *it;
2126 settings::USBDeviceFilter f;
2127 pFilter->i_saveSettings(f);
2128 data.llUSBDeviceFilters.push_back(f);
2129 }
2130
2131 rc = m->pUSBProxyService->i_saveSettings(data.llUSBDeviceSources);
2132 ComAssertComRCRet(rc, rc);
2133#else
2134 RT_NOREF(data);
2135#endif /* VBOX_WITH_USB */
2136
2137#ifdef VBOX_WITH_UPDATE_AGENT
2138 rc = m->pUpdateHost->i_saveSettings(data.updateHost);
2139 ComAssertComRCRet(rc, rc);
2140 /** @todo Add handling for ExtPack and Guest Additions updates here later. See @bugref{7983}. */
2141#endif
2142
2143 return S_OK;
2144}
2145
2146/**
2147 * Sets the given pointer to point to the static list of DVD or floppy
2148 * drives in the Host instance data, depending on the @a mediumType
2149 * parameter.
2150 *
2151 * This builds the list on the first call; it adds or removes host drives
2152 * that may have changed if fRefresh == true.
2153 *
2154 * The caller must hold the medium tree write lock before calling this.
2155 * To protect the list to which the caller's pointer points, the caller
2156 * must also hold that lock.
2157 *
2158 * @param mediumType Must be DeviceType_Floppy or DeviceType_DVD.
2159 * @param fRefresh Whether to refresh the host drives list even if this is not the first call.
2160 * @param pll Caller's pointer which gets set to the static list of host drives.
2161 * @param treeLock Reference to media tree lock, need to drop it temporarily.
2162 * @returns COM status code
2163 */
2164HRESULT Host::i_getDrives(DeviceType_T mediumType,
2165 bool fRefresh,
2166 MediaList *&pll,
2167 AutoWriteLock &treeLock)
2168{
2169 HRESULT rc = S_OK;
2170 Assert(m->pParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
2171
2172 MediaList llNew;
2173 MediaList *pllCached;
2174 bool *pfListBuilt = NULL;
2175
2176 switch (mediumType)
2177 {
2178 case DeviceType_DVD:
2179 if (!m->fDVDDrivesListBuilt || fRefresh)
2180 {
2181 rc = i_buildDVDDrivesList(llNew);
2182 if (FAILED(rc))
2183 return rc;
2184 pfListBuilt = &m->fDVDDrivesListBuilt;
2185 }
2186 pllCached = &m->llDVDDrives;
2187 break;
2188
2189 case DeviceType_Floppy:
2190 if (!m->fFloppyDrivesListBuilt || fRefresh)
2191 {
2192 rc = i_buildFloppyDrivesList(llNew);
2193 if (FAILED(rc))
2194 return rc;
2195 pfListBuilt = &m->fFloppyDrivesListBuilt;
2196 }
2197 pllCached = &m->llFloppyDrives;
2198 break;
2199
2200 default:
2201 return E_INVALIDARG;
2202 }
2203
2204 if (pfListBuilt)
2205 {
2206 // a list was built in llNew above:
2207 if (!*pfListBuilt)
2208 {
2209 // this was the first call (instance bool is still false): then just copy the whole list and return
2210 *pllCached = llNew;
2211 // and mark the instance data as "built"
2212 *pfListBuilt = true;
2213 }
2214 else
2215 {
2216 // list was built, and this was a subsequent call: then compare the old and the new lists
2217
2218 // remove drives from the cached list which are no longer present
2219 for (MediaList::iterator itCached = pllCached->begin();
2220 itCached != pllCached->end();
2221 /*nothing */)
2222 {
2223 Medium *pCached = *itCached;
2224 const Utf8Str strLocationCached = pCached->i_getLocationFull();
2225 bool fFound = false;
2226 for (MediaList::iterator itNew = llNew.begin();
2227 itNew != llNew.end();
2228 ++itNew)
2229 {
2230 Medium *pNew = *itNew;
2231 const Utf8Str strLocationNew = pNew->i_getLocationFull();
2232 if (strLocationNew == strLocationCached)
2233 {
2234 fFound = true;
2235 break;
2236 }
2237 }
2238 if (!fFound)
2239 {
2240 pCached->uninit();
2241 itCached = pllCached->erase(itCached);
2242 }
2243 else
2244 ++itCached;
2245 }
2246
2247 // add drives to the cached list that are not on there yet
2248 for (MediaList::iterator itNew = llNew.begin();
2249 itNew != llNew.end();
2250 ++itNew)
2251 {
2252 Medium *pNew = *itNew;
2253 const Utf8Str strLocationNew = pNew->i_getLocationFull();
2254 bool fFound = false;
2255 for (MediaList::iterator itCached = pllCached->begin();
2256 itCached != pllCached->end();
2257 ++itCached)
2258 {
2259 Medium *pCached = *itCached;
2260 const Utf8Str strLocationCached = pCached->i_getLocationFull();
2261 if (strLocationNew == strLocationCached)
2262 {
2263 fFound = true;
2264 break;
2265 }
2266 }
2267
2268 if (!fFound)
2269 pllCached->push_back(pNew);
2270 }
2271 }
2272 }
2273
2274 // return cached list to caller
2275 pll = pllCached;
2276
2277 // Make sure the media tree lock is released before llNew is cleared,
2278 // as this usually triggers calls to uninit().
2279 treeLock.release();
2280
2281 llNew.clear();
2282
2283 treeLock.acquire();
2284
2285 return rc;
2286}
2287
2288/**
2289 * Goes through the list of host drives that would be returned by getDrives()
2290 * and looks for a host drive with the given UUID. If found, it sets pMedium
2291 * to that drive; otherwise returns VBOX_E_OBJECT_NOT_FOUND.
2292 *
2293 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
2294 * @param uuid Medium UUID of host drive to look for.
2295 * @param fRefresh Whether to refresh the host drives list (see getDrives())
2296 * @param pMedium Medium object, if found...
2297 * @return VBOX_E_OBJECT_NOT_FOUND if not found, or S_OK if found, or errors from getDrives().
2298 */
2299HRESULT Host::i_findHostDriveById(DeviceType_T mediumType,
2300 const Guid &uuid,
2301 bool fRefresh,
2302 ComObjPtr<Medium> &pMedium)
2303{
2304 MediaList *pllMedia;
2305
2306 AutoWriteLock treeLock(m->pParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2307 HRESULT rc = i_getDrives(mediumType, fRefresh, pllMedia, treeLock);
2308 if (SUCCEEDED(rc))
2309 {
2310 for (MediaList::iterator it = pllMedia->begin();
2311 it != pllMedia->end();
2312 ++it)
2313 {
2314 Medium *pThis = *it;
2315 AutoCaller mediumCaller(pThis);
2316 AutoReadLock mediumLock(pThis COMMA_LOCKVAL_SRC_POS);
2317 if (pThis->i_getId() == uuid)
2318 {
2319 pMedium = pThis;
2320 return S_OK;
2321 }
2322 }
2323 }
2324
2325 return VBOX_E_OBJECT_NOT_FOUND;
2326}
2327
2328/**
2329 * Goes through the list of host drives that would be returned by getDrives()
2330 * and looks for a host drive with the given name. If found, it sets pMedium
2331 * to that drive; otherwise returns VBOX_E_OBJECT_NOT_FOUND.
2332 *
2333 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
2334 * @param strLocationFull Name (path) of host drive to look for.
2335 * @param fRefresh Whether to refresh the host drives list (see getDrives())
2336 * @param pMedium Medium object, if found
2337 * @return VBOX_E_OBJECT_NOT_FOUND if not found, or S_OK if found, or errors from getDrives().
2338 */
2339HRESULT Host::i_findHostDriveByName(DeviceType_T mediumType,
2340 const Utf8Str &strLocationFull,
2341 bool fRefresh,
2342 ComObjPtr<Medium> &pMedium)
2343{
2344 MediaList *pllMedia;
2345
2346 AutoWriteLock treeLock(m->pParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2347 HRESULT rc = i_getDrives(mediumType, fRefresh, pllMedia, treeLock);
2348 if (SUCCEEDED(rc))
2349 {
2350 for (MediaList::iterator it = pllMedia->begin();
2351 it != pllMedia->end();
2352 ++it)
2353 {
2354 Medium *pThis = *it;
2355 AutoCaller mediumCaller(pThis);
2356 AutoReadLock mediumLock(pThis COMMA_LOCKVAL_SRC_POS);
2357 if (pThis->i_getLocationFull() == strLocationFull)
2358 {
2359 pMedium = pThis;
2360 return S_OK;
2361 }
2362 }
2363 }
2364
2365 return VBOX_E_OBJECT_NOT_FOUND;
2366}
2367
2368/**
2369 * Goes through the list of host drives that would be returned by getDrives()
2370 * and looks for a host drive with the given name, location or ID. If found,
2371 * it sets pMedium to that drive; otherwise returns VBOX_E_OBJECT_NOT_FOUND.
2372 *
2373 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
2374 * @param strNameOrId Name or full location or UUID of host drive to look for.
2375 * @param pMedium Medium object, if found...
2376 * @return VBOX_E_OBJECT_NOT_FOUND if not found, or S_OK if found, or errors from getDrives().
2377 */
2378HRESULT Host::i_findHostDriveByNameOrId(DeviceType_T mediumType,
2379 const Utf8Str &strNameOrId,
2380 ComObjPtr<Medium> &pMedium)
2381{
2382 AutoWriteLock wlock(m->pParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2383
2384 Guid uuid(strNameOrId);
2385 if (uuid.isValid() && !uuid.isZero())
2386 return i_findHostDriveById(mediumType, uuid, true /* fRefresh */, pMedium);
2387
2388 // string is not a syntactically valid UUID: try a name then
2389 return i_findHostDriveByName(mediumType, strNameOrId, true /* fRefresh */, pMedium);
2390}
2391
2392/**
2393 * Called from getDrives() to build the DVD drives list.
2394 * @param list Media list
2395 * @return
2396 */
2397HRESULT Host::i_buildDVDDrivesList(MediaList &list)
2398{
2399 HRESULT rc = S_OK;
2400
2401 Assert(m->pParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
2402
2403 try
2404 {
2405#if defined(RT_OS_WINDOWS)
2406 int sz = GetLogicalDriveStrings(0, NULL);
2407 TCHAR *hostDrives = new TCHAR[sz+1];
2408 GetLogicalDriveStrings(sz, hostDrives);
2409 wchar_t driveName[3] = { '?', ':', '\0' };
2410 TCHAR *p = hostDrives;
2411 do
2412 {
2413 if (GetDriveType(p) == DRIVE_CDROM)
2414 {
2415 driveName[0] = *p;
2416 ComObjPtr<Medium> hostDVDDriveObj;
2417 hostDVDDriveObj.createObject();
2418 hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(driveName));
2419 list.push_back(hostDVDDriveObj);
2420 }
2421 p += _tcslen(p) + 1;
2422 }
2423 while (*p);
2424 delete[] hostDrives;
2425
2426#elif defined(RT_OS_SOLARIS)
2427# ifdef VBOX_USE_LIBHAL
2428 if (!i_getDVDInfoFromHal(list))
2429# endif
2430 {
2431 i_getDVDInfoFromDevTree(list);
2432 }
2433
2434#elif defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
2435 if (RT_SUCCESS(m->hostDrives.updateDVDs()))
2436 for (DriveInfoList::const_iterator it = m->hostDrives.DVDBegin();
2437 SUCCEEDED(rc) && it != m->hostDrives.DVDEnd(); ++it)
2438 {
2439 ComObjPtr<Medium> hostDVDDriveObj;
2440 Utf8Str location(it->mDevice);
2441 Utf8Str description(it->mDescription);
2442 if (SUCCEEDED(rc))
2443 rc = hostDVDDriveObj.createObject();
2444 if (SUCCEEDED(rc))
2445 rc = hostDVDDriveObj->init(m->pParent, DeviceType_DVD, location, description);
2446 if (SUCCEEDED(rc))
2447 list.push_back(hostDVDDriveObj);
2448 }
2449#elif defined(RT_OS_DARWIN)
2450 PDARWINDVD cur = DarwinGetDVDDrives();
2451 while (cur)
2452 {
2453 ComObjPtr<Medium> hostDVDDriveObj;
2454 hostDVDDriveObj.createObject();
2455 hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(cur->szName));
2456 list.push_back(hostDVDDriveObj);
2457
2458 /* next */
2459 void *freeMe = cur;
2460 cur = cur->pNext;
2461 RTMemFree(freeMe);
2462 }
2463#else
2464 /* PORTME */
2465#endif
2466 }
2467 catch(std::bad_alloc &)
2468 {
2469 rc = E_OUTOFMEMORY;
2470 }
2471 return rc;
2472}
2473
2474/**
2475 * Called from getDrives() to build the floppy drives list.
2476 * @param list
2477 * @return
2478 */
2479HRESULT Host::i_buildFloppyDrivesList(MediaList &list)
2480{
2481 HRESULT rc = S_OK;
2482
2483 Assert(m->pParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
2484
2485 try
2486 {
2487#ifdef RT_OS_WINDOWS
2488 int sz = GetLogicalDriveStrings(0, NULL);
2489 TCHAR *hostDrives = new TCHAR[sz+1];
2490 GetLogicalDriveStrings(sz, hostDrives);
2491 wchar_t driveName[3] = { '?', ':', '\0' };
2492 TCHAR *p = hostDrives;
2493 do
2494 {
2495 if (GetDriveType(p) == DRIVE_REMOVABLE)
2496 {
2497 driveName[0] = *p;
2498 ComObjPtr<Medium> hostFloppyDriveObj;
2499 hostFloppyDriveObj.createObject();
2500 hostFloppyDriveObj->init(m->pParent, DeviceType_Floppy, Bstr(driveName));
2501 list.push_back(hostFloppyDriveObj);
2502 }
2503 p += _tcslen(p) + 1;
2504 }
2505 while (*p);
2506 delete[] hostDrives;
2507#elif defined(RT_OS_LINUX)
2508 if (RT_SUCCESS(m->hostDrives.updateFloppies()))
2509 for (DriveInfoList::const_iterator it = m->hostDrives.FloppyBegin();
2510 SUCCEEDED(rc) && it != m->hostDrives.FloppyEnd(); ++it)
2511 {
2512 ComObjPtr<Medium> hostFloppyDriveObj;
2513 Utf8Str location(it->mDevice);
2514 Utf8Str description(it->mDescription);
2515 if (SUCCEEDED(rc))
2516 rc = hostFloppyDriveObj.createObject();
2517 if (SUCCEEDED(rc))
2518 rc = hostFloppyDriveObj->init(m->pParent, DeviceType_Floppy, location, description);
2519 if (SUCCEEDED(rc))
2520 list.push_back(hostFloppyDriveObj);
2521 }
2522#else
2523 RT_NOREF(list);
2524 /* PORTME */
2525#endif
2526 }
2527 catch(std::bad_alloc &)
2528 {
2529 rc = E_OUTOFMEMORY;
2530 }
2531
2532 return rc;
2533}
2534
2535#ifdef VBOX_WITH_USB
2536USBProxyService* Host::i_usbProxyService()
2537{
2538 return m->pUSBProxyService;
2539}
2540
2541HRESULT Host::i_addChild(HostUSBDeviceFilter *pChild)
2542{
2543 AutoCaller autoCaller(this);
2544 if (FAILED(autoCaller.rc()))
2545 return autoCaller.rc();
2546
2547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2548
2549 m->llChildren.push_back(pChild);
2550
2551 return S_OK;
2552}
2553
2554HRESULT Host::i_removeChild(HostUSBDeviceFilter *pChild)
2555{
2556 AutoCaller autoCaller(this);
2557 if (FAILED(autoCaller.rc()))
2558 return autoCaller.rc();
2559
2560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2561
2562 for (USBDeviceFilterList::iterator it = m->llChildren.begin();
2563 it != m->llChildren.end();
2564 ++it)
2565 {
2566 if (*it == pChild)
2567 {
2568 m->llChildren.erase(it);
2569 break;
2570 }
2571 }
2572
2573 return S_OK;
2574}
2575
2576VirtualBox* Host::i_parent()
2577{
2578 return m->pParent;
2579}
2580
2581/**
2582 * Called by setter methods of all USB device filters.
2583 */
2584HRESULT Host::i_onUSBDeviceFilterChange(HostUSBDeviceFilter *aFilter,
2585 BOOL aActiveChanged /* = FALSE */)
2586{
2587 AutoCaller autoCaller(this);
2588 if (FAILED(autoCaller.rc()))
2589 return autoCaller.rc();
2590
2591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 if (aFilter->mInList)
2594 {
2595 if (aActiveChanged)
2596 {
2597 // insert/remove the filter from the proxy
2598 if (aFilter->i_getData().mData.fActive)
2599 {
2600 ComAssertRet(aFilter->i_getId() == NULL, E_FAIL);
2601 aFilter->i_getId() = m->pUSBProxyService->insertFilter(&aFilter->i_getData().mUSBFilter);
2602 }
2603 else
2604 {
2605 ComAssertRet(aFilter->i_getId() != NULL, E_FAIL);
2606 m->pUSBProxyService->removeFilter(aFilter->i_getId());
2607 aFilter->i_getId() = NULL;
2608 }
2609 }
2610 else
2611 {
2612 if (aFilter->i_getData().mData.fActive)
2613 {
2614 // update the filter in the proxy
2615 ComAssertRet(aFilter->i_getId() != NULL, E_FAIL);
2616 m->pUSBProxyService->removeFilter(aFilter->i_getId());
2617 aFilter->i_getId() = m->pUSBProxyService->insertFilter(&aFilter->i_getData().mUSBFilter);
2618 }
2619 }
2620
2621 // save the global settings... yeah, on every single filter property change
2622 // for that we should hold only the VirtualBox lock
2623 alock.release();
2624 AutoWriteLock vboxLock(m->pParent COMMA_LOCKVAL_SRC_POS);
2625 return m->pParent->i_saveSettings();
2626 }
2627
2628 return S_OK;
2629}
2630
2631
2632/**
2633 * Interface for obtaining a copy of the USBDeviceFilterList,
2634 * used by the USBProxyService.
2635 *
2636 * @param aGlobalFilters Where to put the global filter list copy.
2637 * @param aMachines Where to put the machine vector.
2638 */
2639void Host::i_getUSBFilters(Host::USBDeviceFilterList *aGlobalFilters)
2640{
2641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2642
2643 *aGlobalFilters = m->llUSBDeviceFilters;
2644}
2645
2646#endif /* VBOX_WITH_USB */
2647
2648// private methods
2649////////////////////////////////////////////////////////////////////////////////
2650
2651#if defined(RT_OS_SOLARIS) && defined(VBOX_USE_LIBHAL)
2652
2653/**
2654 * Helper function to get the slice number from a device path
2655 *
2656 * @param pszDevLinkPath Pointer to a device path (/dev/(r)dsk/c7d1t0d0s3 etc.)
2657 * @returns Pointer to the slice portion of the given path.
2658 */
2659static char *solarisGetSliceFromPath(const char *pszDevLinkPath)
2660{
2661 char *pszSlice = (char *)strrchr(pszDevLinkPath, 's');
2662 char *pszDisk = (char *)strrchr(pszDevLinkPath, 'd');
2663 char *pszFound;
2664 if (pszSlice && (uintptr_t)pszSlice > (uintptr_t)pszDisk)
2665 pszFound = pszSlice;
2666 else
2667 pszFound = pszDisk;
2668
2669 if (pszFound && RT_C_IS_DIGIT(pszFound[1]))
2670 return pszFound;
2671
2672 return NULL;
2673}
2674
2675/**
2676 * Walk device links and returns an allocated path for the first one in the snapshot.
2677 *
2678 * @param DevLink Handle to the device link being walked.
2679 * @param pvArg Opaque pointer that we use to point to the return
2680 * variable (char *). Caller must call RTStrFree on it.
2681 * @returns DI_WALK_TERMINATE to stop the walk.
2682 */
2683static int solarisWalkDevLink(di_devlink_t DevLink, void *pvArg)
2684{
2685 char **ppszPath = (char **)pvArg;
2686 *ppszPath = RTStrDup(di_devlink_path(DevLink));
2687 return DI_WALK_TERMINATE;
2688}
2689
2690/**
2691 * Walk all devices in the system and enumerate CD/DVD drives.
2692 * @param Node Handle to the current node.
2693 * @param pvArg Opaque data (holds list pointer).
2694 * @returns Solaris specific code whether to continue walking or not.
2695 */
2696static int solarisWalkDeviceNodeForDVD(di_node_t Node, void *pvArg)
2697{
2698 PSOLARISDVD *ppDrives = (PSOLARISDVD *)pvArg;
2699
2700 /*
2701 * Check for "removable-media" or "hotpluggable" instead of "SCSI" so that we also include USB CD-ROMs.
2702 * As unfortunately the Solaris drivers only export these common properties.
2703 */
2704 int *pInt = NULL;
2705 if ( di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "removable-media", &pInt) >= 0
2706 || di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "hotpluggable", &pInt) >= 0)
2707 {
2708 if (di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "inquiry-device-type", &pInt) > 0
2709 && ( *pInt == DTYPE_RODIRECT /* CDROM */
2710 || *pInt == DTYPE_OPTICAL)) /* Optical Drive */
2711 {
2712 char *pszProduct = NULL;
2713 if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "inquiry-product-id", &pszProduct) > 0)
2714 {
2715 char *pszVendor = NULL;
2716 if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "inquiry-vendor-id", &pszVendor) > 0)
2717 {
2718 /*
2719 * Found a DVD drive, we need to scan the minor nodes to find the correct
2720 * slice that represents the whole drive. "s2" is always the whole drive for CD/DVDs.
2721 */
2722 int Major = di_driver_major(Node);
2723 di_minor_t Minor = DI_MINOR_NIL;
2724 di_devlink_handle_t DevLink = di_devlink_init(NULL /* name */, 0 /* flags */);
2725 if (DevLink)
2726 {
2727 while ((Minor = di_minor_next(Node, Minor)) != DI_MINOR_NIL)
2728 {
2729 dev_t Dev = di_minor_devt(Minor);
2730 if ( Major != (int)major(Dev)
2731 || di_minor_spectype(Minor) == S_IFBLK
2732 || di_minor_type(Minor) != DDM_MINOR)
2733 {
2734 continue;
2735 }
2736
2737 char *pszMinorPath = di_devfs_minor_path(Minor);
2738 if (!pszMinorPath)
2739 continue;
2740
2741 char *pszDevLinkPath = NULL;
2742 di_devlink_walk(DevLink, NULL, pszMinorPath, DI_PRIMARY_LINK, &pszDevLinkPath, solarisWalkDevLink);
2743 di_devfs_path_free(pszMinorPath);
2744
2745 if (pszDevLinkPath)
2746 {
2747 char *pszSlice = solarisGetSliceFromPath(pszDevLinkPath);
2748 if ( pszSlice && !strcmp(pszSlice, "s2")
2749 && !strncmp(pszDevLinkPath, RT_STR_TUPLE("/dev/rdsk"))) /* We want only raw disks */
2750 {
2751 /*
2752 * We've got a fully qualified DVD drive. Add it to the list.
2753 */
2754 PSOLARISDVD pDrive = (PSOLARISDVD)RTMemAllocZ(sizeof(SOLARISDVD));
2755 if (RT_LIKELY(pDrive))
2756 {
2757 RTStrPrintf(pDrive->szDescription, sizeof(pDrive->szDescription),
2758 "%s %s", pszVendor, pszProduct);
2759 RTStrPurgeEncoding(pDrive->szDescription);
2760 RTStrCopy(pDrive->szRawDiskPath, sizeof(pDrive->szRawDiskPath), pszDevLinkPath);
2761 if (*ppDrives)
2762 pDrive->pNext = *ppDrives;
2763 *ppDrives = pDrive;
2764
2765 /* We're not interested in any of the other slices, stop minor nodes traversal. */
2766 RTStrFree(pszDevLinkPath);
2767 break;
2768 }
2769 }
2770 RTStrFree(pszDevLinkPath);
2771 }
2772 }
2773 di_devlink_fini(&DevLink);
2774 }
2775 }
2776 }
2777 }
2778 }
2779 return DI_WALK_CONTINUE;
2780}
2781
2782/**
2783 * Solaris specific function to enumerate CD/DVD drives via the device tree.
2784 * Works on Solaris 10 as well as OpenSolaris without depending on libhal.
2785 */
2786void Host::i_getDVDInfoFromDevTree(std::list<ComObjPtr<Medium> > &list)
2787{
2788 PSOLARISDVD pDrives = NULL;
2789 di_node_t RootNode = di_init("/", DINFOCPYALL);
2790 if (RootNode != DI_NODE_NIL)
2791 di_walk_node(RootNode, DI_WALK_CLDFIRST, &pDrives, solarisWalkDeviceNodeForDVD);
2792
2793 di_fini(RootNode);
2794
2795 while (pDrives)
2796 {
2797 ComObjPtr<Medium> hostDVDDriveObj;
2798 hostDVDDriveObj.createObject();
2799 hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(pDrives->szRawDiskPath), Bstr(pDrives->szDescription));
2800 list.push_back(hostDVDDriveObj);
2801
2802 void *pvDrive = pDrives;
2803 pDrives = pDrives->pNext;
2804 RTMemFree(pvDrive);
2805 }
2806}
2807
2808
2809/**
2810 * Walk all devices in the system and enumerate fixed drives.
2811 * @param Node Handle to the current node.
2812 * @param pvArg Opaque data (holds list pointer).
2813 * @returns Solaris specific code whether to continue walking or not.
2814 */
2815static int solarisWalkDeviceNodeForFixedDrive(di_node_t Node, void *pvArg) RT_NOEXCEPT
2816{
2817 PSOLARISFIXEDDISK *ppDrives = (PSOLARISFIXEDDISK *)pvArg;
2818
2819 int *pInt = NULL;
2820 if ( di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "inquiry-device-type", &pInt) > 0
2821 && *pInt == DTYPE_DIRECT) /* Fixed drive */
2822 {
2823 char *pszProduct = NULL;
2824 if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "inquiry-product-id", &pszProduct) > 0)
2825 {
2826 char *pszVendor = NULL;
2827 if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "inquiry-vendor-id", &pszVendor) > 0)
2828 {
2829 /*
2830 * Found a fixed drive, we need to scan the minor nodes to find the correct
2831 * slice that represents the whole drive.
2832 */
2833 int Major = di_driver_major(Node);
2834 di_minor_t Minor = DI_MINOR_NIL;
2835 di_devlink_handle_t DevLink = di_devlink_init(NULL /* name */, 0 /* flags */);
2836 if (DevLink)
2837 {
2838 /*
2839 * The device name we have to select depends on drive type. For fixed drives, the
2840 * name without slice or partition should be selected, for USB flash drive the
2841 * partition 0 should be selected and slice 0 for other cases.
2842 */
2843 char *pszDisk = NULL;
2844 char *pszPartition0 = NULL;
2845 char *pszSlice0 = NULL;
2846 while ((Minor = di_minor_next(Node, Minor)) != DI_MINOR_NIL)
2847 {
2848 dev_t Dev = di_minor_devt(Minor);
2849 if ( Major != (int)major(Dev)
2850 || di_minor_spectype(Minor) == S_IFBLK
2851 || di_minor_type(Minor) != DDM_MINOR)
2852 continue;
2853
2854 char *pszMinorPath = di_devfs_minor_path(Minor);
2855 if (!pszMinorPath)
2856 continue;
2857
2858 char *pszDevLinkPath = NULL;
2859 di_devlink_walk(DevLink, NULL, pszMinorPath, DI_PRIMARY_LINK, &pszDevLinkPath, solarisWalkDevLink);
2860 di_devfs_path_free(pszMinorPath);
2861
2862 if (pszDevLinkPath)
2863 {
2864 char const *pszCurSlice = strrchr(pszDevLinkPath, 's');
2865 char const *pszCurDisk = strrchr(pszDevLinkPath, 'd');
2866 char const *pszCurPart = strrchr(pszDevLinkPath, 'p');
2867 char **ppszDst = NULL;
2868 if (pszCurSlice && (uintptr_t)pszCurSlice > (uintptr_t)pszCurDisk && !strcmp(pszCurSlice, "s0"))
2869 ppszDst = &pszSlice0;
2870 else if (pszCurPart && (uintptr_t)pszCurPart > (uintptr_t)pszCurDisk && !strcmp(pszCurPart, "p0"))
2871 ppszDst = &pszPartition0;
2872 else if ( (!pszCurSlice || (uintptr_t)pszCurSlice < (uintptr_t)pszCurDisk)
2873 && (!pszCurPart || (uintptr_t)pszCurPart < (uintptr_t)pszCurDisk)
2874 && *pszDevLinkPath != '\0')
2875 ppszDst = &pszDisk;
2876 else
2877 RTStrFree(pszDevLinkPath);
2878 if (ppszDst)
2879 {
2880 if (*ppszDst != NULL)
2881 RTStrFree(*ppszDst);
2882 *ppszDst = pszDevLinkPath;
2883 }
2884 }
2885 }
2886 di_devlink_fini(&DevLink);
2887 if (pszDisk || pszPartition0 || pszSlice0)
2888 {
2889 PSOLARISFIXEDDISK pDrive = (PSOLARISFIXEDDISK)RTMemAllocZ(sizeof(*pDrive));
2890 if (RT_LIKELY(pDrive))
2891 {
2892 RTStrPrintf(pDrive->szDescription, sizeof(pDrive->szDescription), "%s %s", pszVendor, pszProduct);
2893 RTStrPurgeEncoding(pDrive->szDescription);
2894
2895 const char *pszDevPath = pszDisk ? pszDisk : pszPartition0 ? pszPartition0 : pszSlice0;
2896 int rc = RTStrCopy(pDrive->szRawDiskPath, sizeof(pDrive->szRawDiskPath), pszDevPath);
2897 AssertRC(rc);
2898
2899 if (*ppDrives)
2900 pDrive->pNext = *ppDrives;
2901 *ppDrives = pDrive;
2902 }
2903 RTStrFree(pszDisk);
2904 RTStrFree(pszPartition0);
2905 RTStrFree(pszSlice0);
2906 }
2907 }
2908 }
2909 }
2910 }
2911 return DI_WALK_CONTINUE;
2912}
2913
2914
2915/**
2916 * Solaris specific function to enumerate fixed drives via the device tree.
2917 * Works on Solaris 10 as well as OpenSolaris without depending on libhal.
2918 *
2919 * @returns COM status, either S_OK or E_OUTOFMEMORY.
2920 * @param list Reference to list where the the path/model pairs are to
2921 * be returned.
2922 */
2923HRESULT Host::i_getFixedDrivesFromDevTree(std::list<std::pair<com::Utf8Str, com::Utf8Str> > &list) RT_NOEXCEPT
2924{
2925 PSOLARISFIXEDDISK pDrives = NULL;
2926 di_node_t RootNode = di_init("/", DINFOCPYALL);
2927 if (RootNode != DI_NODE_NIL)
2928 di_walk_node(RootNode, DI_WALK_CLDFIRST, &pDrives, solarisWalkDeviceNodeForFixedDrive);
2929 di_fini(RootNode);
2930
2931 HRESULT hrc = S_OK;
2932 try
2933 {
2934 for (PSOLARISFIXEDDISK pCurDrv = pDrives; pCurDrv; pCurDrv = pCurDrv->pNext)
2935 list.push_back(std::pair<com::Utf8Str, com::Utf8Str>(pCurDrv->szRawDiskPath, pCurDrv->szDescription));
2936 }
2937 catch (std::bad_alloc &)
2938 {
2939 LogRelFunc(("Out of memory!\n"));
2940 list.clear();
2941 hrc = E_OUTOFMEMORY;
2942 }
2943
2944 while (pDrives)
2945 {
2946 PSOLARISFIXEDDISK pFreeMe = pDrives;
2947 pDrives = pDrives->pNext;
2948 ASMCompilerBarrier();
2949 RTMemFree(pFreeMe);
2950 }
2951
2952 return hrc;
2953}
2954
2955
2956/* Solaris hosts, loading libhal at runtime */
2957
2958/**
2959 * Helper function to query the hal subsystem for information about DVD drives attached to the
2960 * system.
2961 *
2962 * @returns true if information was successfully obtained, false otherwise
2963 * @param list Reference to list where the DVDs drives are to be returned.
2964 */
2965bool Host::i_getDVDInfoFromHal(std::list<ComObjPtr<Medium> > &list)
2966{
2967 bool halSuccess = false;
2968 DBusError dbusError;
2969 if (!gLibHalCheckPresence())
2970 return false;
2971 gDBusErrorInit(&dbusError);
2972 DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError);
2973 if (dbusConnection != 0)
2974 {
2975 LibHalContext *halContext = gLibHalCtxNew();
2976 if (halContext != 0)
2977 {
2978 if (gLibHalCtxSetDBusConnection(halContext, dbusConnection))
2979 {
2980 if (gLibHalCtxInit(halContext, &dbusError))
2981 {
2982 int numDevices;
2983 char **halDevices = gLibHalFindDeviceStringMatch(halContext,
2984 "storage.drive_type", "cdrom",
2985 &numDevices, &dbusError);
2986 if (halDevices != 0)
2987 {
2988 /* Hal is installed and working, so if no devices are reported, assume
2989 that there are none. */
2990 halSuccess = true;
2991 for (int i = 0; i < numDevices; i++)
2992 {
2993 char *devNode = gLibHalDeviceGetPropertyString(halContext,
2994 halDevices[i], "block.device", &dbusError);
2995#ifdef RT_OS_SOLARIS
2996 /* The CD/DVD ioctls work only for raw device nodes. */
2997 char *tmp = getfullrawname(devNode);
2998 gLibHalFreeString(devNode);
2999 devNode = tmp;
3000#endif
3001
3002 if (devNode != 0)
3003 {
3004// if (validateDevice(devNode, true))
3005// {
3006 Utf8Str description;
3007 char *vendor, *product;
3008 /* We do not check the error here, as this field may
3009 not even exist. */
3010 vendor = gLibHalDeviceGetPropertyString(halContext,
3011 halDevices[i], "info.vendor", 0);
3012 product = gLibHalDeviceGetPropertyString(halContext,
3013 halDevices[i], "info.product", &dbusError);
3014 if ((product != 0 && product[0] != 0))
3015 {
3016 if ((vendor != 0) && (vendor[0] != 0))
3017 {
3018 description = Utf8StrFmt("%s %s",
3019 vendor, product);
3020 }
3021 else
3022 {
3023 description = product;
3024 }
3025 ComObjPtr<Medium> hostDVDDriveObj;
3026 hostDVDDriveObj.createObject();
3027 hostDVDDriveObj->init(m->pParent, DeviceType_DVD,
3028 Bstr(devNode), Bstr(description));
3029 list.push_back(hostDVDDriveObj);
3030 }
3031 else
3032 {
3033 if (product == 0)
3034 {
3035 LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
3036 halDevices[i], dbusError.name, dbusError.message));
3037 gDBusErrorFree(&dbusError);
3038 }
3039 ComObjPtr<Medium> hostDVDDriveObj;
3040 hostDVDDriveObj.createObject();
3041 hostDVDDriveObj->init(m->pParent, DeviceType_DVD,
3042 Bstr(devNode));
3043 list.push_back(hostDVDDriveObj);
3044 }
3045 if (vendor != 0)
3046 {
3047 gLibHalFreeString(vendor);
3048 }
3049 if (product != 0)
3050 {
3051 gLibHalFreeString(product);
3052 }
3053// }
3054// else
3055// {
3056// LogRel(("Host::COMGETTER(DVDDrives): failed to validate the block device %s as a DVD drive\n"));
3057// }
3058#ifndef RT_OS_SOLARIS
3059 gLibHalFreeString(devNode);
3060#else
3061 free(devNode);
3062#endif
3063 }
3064 else
3065 {
3066 LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
3067 halDevices[i], dbusError.name, dbusError.message));
3068 gDBusErrorFree(&dbusError);
3069 }
3070 }
3071 gLibHalFreeStringArray(halDevices);
3072 }
3073 else
3074 {
3075 LogRel(("Host::COMGETTER(DVDDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message));
3076 gDBusErrorFree(&dbusError);
3077 }
3078 if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */
3079 {
3080 LogRel(("Host::COMGETTER(DVDDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n",
3081 dbusError.name, dbusError.message));
3082 gDBusErrorFree(&dbusError);
3083 }
3084 }
3085 else
3086 {
3087 LogRel(("Host::COMGETTER(DVDDrives): failed to initialise libhal context. dbus error: %s (%s)\n",
3088 dbusError.name, dbusError.message));
3089 gDBusErrorFree(&dbusError);
3090 }
3091 gLibHalCtxFree(halContext);
3092 }
3093 else
3094 {
3095 LogRel(("Host::COMGETTER(DVDDrives): failed to set libhal connection to dbus.\n"));
3096 }
3097 }
3098 else
3099 {
3100 LogRel(("Host::COMGETTER(DVDDrives): failed to get a libhal context - out of memory?\n"));
3101 }
3102 gDBusConnectionUnref(dbusConnection);
3103 }
3104 else
3105 {
3106 LogRel(("Host::COMGETTER(DVDDrives): failed to connect to dbus. dbus error: %s (%s)\n",
3107 dbusError.name, dbusError.message));
3108 gDBusErrorFree(&dbusError);
3109 }
3110 return halSuccess;
3111}
3112
3113
3114/**
3115 * Helper function to query the hal subsystem for information about floppy drives attached to the
3116 * system.
3117 *
3118 * @returns true if information was successfully obtained, false otherwise
3119 * @retval list drives found will be attached to this list
3120 */
3121bool Host::i_getFloppyInfoFromHal(std::list< ComObjPtr<Medium> > &list)
3122{
3123 bool halSuccess = false;
3124 DBusError dbusError;
3125 if (!gLibHalCheckPresence())
3126 return false;
3127 gDBusErrorInit(&dbusError);
3128 DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError);
3129 if (dbusConnection != 0)
3130 {
3131 LibHalContext *halContext = gLibHalCtxNew();
3132 if (halContext != 0)
3133 {
3134 if (gLibHalCtxSetDBusConnection(halContext, dbusConnection))
3135 {
3136 if (gLibHalCtxInit(halContext, &dbusError))
3137 {
3138 int numDevices;
3139 char **halDevices = gLibHalFindDeviceStringMatch(halContext,
3140 "storage.drive_type", "floppy",
3141 &numDevices, &dbusError);
3142 if (halDevices != 0)
3143 {
3144 /* Hal is installed and working, so if no devices are reported, assume
3145 that there are none. */
3146 halSuccess = true;
3147 for (int i = 0; i < numDevices; i++)
3148 {
3149 char *driveType = gLibHalDeviceGetPropertyString(halContext,
3150 halDevices[i], "storage.drive_type", 0);
3151 if (driveType != 0)
3152 {
3153 if (strcmp(driveType, "floppy") != 0)
3154 {
3155 gLibHalFreeString(driveType);
3156 continue;
3157 }
3158 gLibHalFreeString(driveType);
3159 }
3160 else
3161 {
3162 /* An error occurred. The attribute "storage.drive_type"
3163 probably didn't exist. */
3164 continue;
3165 }
3166 char *devNode = gLibHalDeviceGetPropertyString(halContext,
3167 halDevices[i], "block.device", &dbusError);
3168 if (devNode != 0)
3169 {
3170// if (validateDevice(devNode, false))
3171// {
3172 Utf8Str description;
3173 char *vendor, *product;
3174 /* We do not check the error here, as this field may
3175 not even exist. */
3176 vendor = gLibHalDeviceGetPropertyString(halContext,
3177 halDevices[i], "info.vendor", 0);
3178 product = gLibHalDeviceGetPropertyString(halContext,
3179 halDevices[i], "info.product", &dbusError);
3180 if ((product != 0) && (product[0] != 0))
3181 {
3182 if ((vendor != 0) && (vendor[0] != 0))
3183 {
3184 description = Utf8StrFmt("%s %s",
3185 vendor, product);
3186 }
3187 else
3188 {
3189 description = product;
3190 }
3191 ComObjPtr<Medium> hostFloppyDrive;
3192 hostFloppyDrive.createObject();
3193 hostFloppyDrive->init(m->pParent, DeviceType_DVD,
3194 Bstr(devNode), Bstr(description));
3195 list.push_back(hostFloppyDrive);
3196 }
3197 else
3198 {
3199 if (product == 0)
3200 {
3201 LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
3202 halDevices[i], dbusError.name, dbusError.message));
3203 gDBusErrorFree(&dbusError);
3204 }
3205 ComObjPtr<Medium> hostFloppyDrive;
3206 hostFloppyDrive.createObject();
3207 hostFloppyDrive->init(m->pParent, DeviceType_DVD,
3208 Bstr(devNode));
3209 list.push_back(hostFloppyDrive);
3210 }
3211 if (vendor != 0)
3212 {
3213 gLibHalFreeString(vendor);
3214 }
3215 if (product != 0)
3216 {
3217 gLibHalFreeString(product);
3218 }
3219// }
3220// else
3221// {
3222// LogRel(("Host::COMGETTER(FloppyDrives): failed to validate the block device %s as a floppy drive\n"));
3223// }
3224 gLibHalFreeString(devNode);
3225 }
3226 else
3227 {
3228 LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
3229 halDevices[i], dbusError.name, dbusError.message));
3230 gDBusErrorFree(&dbusError);
3231 }
3232 }
3233 gLibHalFreeStringArray(halDevices);
3234 }
3235 else
3236 {
3237 LogRel(("Host::COMGETTER(FloppyDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message));
3238 gDBusErrorFree(&dbusError);
3239 }
3240 if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */
3241 {
3242 LogRel(("Host::COMGETTER(FloppyDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n",
3243 dbusError.name, dbusError.message));
3244 gDBusErrorFree(&dbusError);
3245 }
3246 }
3247 else
3248 {
3249 LogRel(("Host::COMGETTER(FloppyDrives): failed to initialise libhal context. dbus error: %s (%s)\n",
3250 dbusError.name, dbusError.message));
3251 gDBusErrorFree(&dbusError);
3252 }
3253 gLibHalCtxFree(halContext);
3254 }
3255 else
3256 {
3257 LogRel(("Host::COMGETTER(FloppyDrives): failed to set libhal connection to dbus.\n"));
3258 }
3259 }
3260 else
3261 {
3262 LogRel(("Host::COMGETTER(FloppyDrives): failed to get a libhal context - out of memory?\n"));
3263 }
3264 gDBusConnectionUnref(dbusConnection);
3265 }
3266 else
3267 {
3268 LogRel(("Host::COMGETTER(FloppyDrives): failed to connect to dbus. dbus error: %s (%s)\n",
3269 dbusError.name, dbusError.message));
3270 gDBusErrorFree(&dbusError);
3271 }
3272 return halSuccess;
3273}
3274
3275
3276/**
3277 * Helper function to query the hal subsystem for information about fixed drives attached to the
3278 * system.
3279 *
3280 * @returns COM status code. (setError is not called on failure as we only fail
3281 * with E_OUTOFMEMORY.)
3282 * @retval S_OK on success.
3283 * @retval S_FALSE if HAL cannot be used.
3284 * @param list Reference to list to return the path/model string pairs.
3285 */
3286HRESULT Host::i_getFixedDrivesFromHal(std::list<std::pair<com::Utf8Str, com::Utf8Str> > &list) RT_NOEXCEPT
3287{
3288 HRESULT hrc = S_FALSE;
3289 if (!gLibHalCheckPresence())
3290 return hrc;
3291
3292 DBusError dbusError;
3293 gDBusErrorInit(&dbusError);
3294 DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError);
3295 if (dbusConnection != 0)
3296 {
3297 LibHalContext *halContext = gLibHalCtxNew();
3298 if (halContext != 0)
3299 {
3300 if (gLibHalCtxSetDBusConnection(halContext, dbusConnection))
3301 {
3302 if (gLibHalCtxInit(halContext, &dbusError))
3303 {
3304 int cDevices;
3305 char **halDevices = gLibHalFindDeviceStringMatch(halContext, "storage.drive_type", "disk",
3306 &cDevices, &dbusError);
3307 if (halDevices != 0)
3308 {
3309 /* Hal is installed and working, so if no devices are reported, assume
3310 that there are none. */
3311 hrc = S_OK;
3312 for (int i = 0; i < cDevices && hrc == S_OK; i++)
3313 {
3314 char *pszDevNode = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "block.device",
3315 &dbusError);
3316 /* The fixed drive ioctls work only for raw device nodes. */
3317 char *pszTmp = getfullrawname(pszDevNode);
3318 gLibHalFreeString(pszDevNode);
3319 pszDevNode = pszTmp;
3320 if (pszDevNode != 0)
3321 {
3322 /* We do not check the error here, as this field may
3323 not even exist. */
3324 char *pszVendor = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "info.vendor", 0);
3325 char *pszProduct = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "info.product",
3326 &dbusError);
3327 Utf8Str strDescription;
3328 if (pszProduct != NULL && pszProduct[0] != '\0')
3329 {
3330 int vrc;
3331 if (pszVendor != NULL && pszVendor[0] != '\0')
3332 vrc = strDescription.printfNoThrow("%s %s", pszVendor, pszProduct);
3333 else
3334 vrc = strDescription.assignNoThrow(pszProduct);
3335 AssertRCStmt(vrc, hrc = E_OUTOFMEMORY);
3336 }
3337 if (pszVendor != NULL)
3338 gLibHalFreeString(pszVendor);
3339 if (pszProduct != NULL)
3340 gLibHalFreeString(pszProduct);
3341
3342 /* Correct device/partition/slice already choosen. Just add it to the return list */
3343 if (hrc == S_OK)
3344 try
3345 {
3346 list.push_back(std::pair<com::Utf8Str, com::Utf8Str>(pszDevNode, strDescription));
3347 }
3348 catch (std::bad_alloc &)
3349 {
3350 AssertFailedStmt(hrc = E_OUTOFMEMORY);
3351 }
3352 gLibHalFreeString(pszDevNode);
3353 }
3354 else
3355 {
3356 LogRel(("Host::COMGETTER(HostDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
3357 halDevices[i], dbusError.name, dbusError.message));
3358 gDBusErrorFree(&dbusError);
3359 }
3360 }
3361 gLibHalFreeStringArray(halDevices);
3362 }
3363 else
3364 {
3365 LogRel(("Host::COMGETTER(HostDrives): failed to get devices with capability \"storage.disk\". dbus error: %s (%s)\n", dbusError.name, dbusError.message));
3366 gDBusErrorFree(&dbusError);
3367 }
3368 if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */
3369 {
3370 LogRel(("Host::COMGETTER(HostDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n",
3371 dbusError.name, dbusError.message));
3372 gDBusErrorFree(&dbusError);
3373 }
3374 }
3375 else
3376 {
3377 LogRel(("Host::COMGETTER(HostDrives): failed to initialise libhal context. dbus error: %s (%s)\n",
3378 dbusError.name, dbusError.message));
3379 gDBusErrorFree(&dbusError);
3380 }
3381 gLibHalCtxFree(halContext);
3382 }
3383 else
3384 LogRel(("Host::COMGETTER(HostDrives): failed to set libhal connection to dbus.\n"));
3385 }
3386 else
3387 LogRel(("Host::COMGETTER(HostDrives): failed to get a libhal context - out of memory?\n"));
3388 gDBusConnectionUnref(dbusConnection);
3389 }
3390 else
3391 {
3392 LogRel(("Host::COMGETTER(HostDrives): failed to connect to dbus. dbus error: %s (%s)\n",
3393 dbusError.name, dbusError.message));
3394 gDBusErrorFree(&dbusError);
3395 }
3396 return hrc;
3397}
3398
3399#endif /* RT_OS_SOLARIS and VBOX_USE_HAL */
3400
3401/** @todo get rid of dead code below - RT_OS_SOLARIS and RT_OS_LINUX are never both set */
3402#if defined(RT_OS_SOLARIS)
3403
3404/**
3405 * Helper function to parse the given mount file and add found entries
3406 */
3407void Host::i_parseMountTable(char *mountTable, std::list< ComObjPtr<Medium> > &list)
3408{
3409#ifdef RT_OS_LINUX
3410 FILE *mtab = setmntent(mountTable, "r");
3411 if (mtab)
3412 {
3413 struct mntent *mntent;
3414 char *mnt_type;
3415 char *mnt_dev;
3416 char *tmp;
3417 while ((mntent = getmntent(mtab)))
3418 {
3419 mnt_type = (char*)malloc(strlen(mntent->mnt_type) + 1);
3420 mnt_dev = (char*)malloc(strlen(mntent->mnt_fsname) + 1);
3421 strcpy(mnt_type, mntent->mnt_type);
3422 strcpy(mnt_dev, mntent->mnt_fsname);
3423 // supermount fs case
3424 if (strcmp(mnt_type, "supermount") == 0)
3425 {
3426 tmp = strstr(mntent->mnt_opts, "fs=");
3427 if (tmp)
3428 {
3429 free(mnt_type);
3430 mnt_type = strdup(tmp + strlen("fs="));
3431 if (mnt_type)
3432 {
3433 tmp = strchr(mnt_type, ',');
3434 if (tmp)
3435 *tmp = '\0';
3436 }
3437 }
3438 tmp = strstr(mntent->mnt_opts, "dev=");
3439 if (tmp)
3440 {
3441 free(mnt_dev);
3442 mnt_dev = strdup(tmp + strlen("dev="));
3443 if (mnt_dev)
3444 {
3445 tmp = strchr(mnt_dev, ',');
3446 if (tmp)
3447 *tmp = '\0';
3448 }
3449 }
3450 }
3451 // use strstr here to cover things fs types like "udf,iso9660"
3452 if (strstr(mnt_type, "iso9660") == 0)
3453 {
3454 /** @todo check whether we've already got the drive in our list! */
3455 if (i_validateDevice(mnt_dev, true))
3456 {
3457 ComObjPtr<Medium> hostDVDDriveObj;
3458 hostDVDDriveObj.createObject();
3459 hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(mnt_dev));
3460 list.push_back (hostDVDDriveObj);
3461 }
3462 }
3463 free(mnt_dev);
3464 free(mnt_type);
3465 }
3466 endmntent(mtab);
3467 }
3468#else // RT_OS_SOLARIS
3469 FILE *mntFile = fopen(mountTable, "r");
3470 if (mntFile)
3471 {
3472 struct mnttab mntTab;
3473 while (getmntent(mntFile, &mntTab) == 0)
3474 {
3475 const char *mountName = mntTab.mnt_special;
3476 const char *mountPoint = mntTab.mnt_mountp;
3477 const char *mountFSType = mntTab.mnt_fstype;
3478 if (mountName && mountPoint && mountFSType)
3479 {
3480 // skip devices we are not interested in
3481 if ((*mountName && mountName[0] == '/') && // skip 'fake' devices (like -hosts,
3482 // proc, fd, swap)
3483 (*mountFSType && (strncmp(mountFSType, RT_STR_TUPLE("devfs")) != 0 && // skip devfs
3484 // (i.e. /devices)
3485 strncmp(mountFSType, RT_STR_TUPLE("dev")) != 0 && // skip dev (i.e. /dev)
3486 strncmp(mountFSType, RT_STR_TUPLE("lofs")) != 0))) // skip loop-back file-system (lofs)
3487 {
3488 char *rawDevName = getfullrawname((char *)mountName);
3489 if (i_validateDevice(rawDevName, true))
3490 {
3491 ComObjPtr<Medium> hostDVDDriveObj;
3492 hostDVDDriveObj.createObject();
3493 hostDVDDriveObj->init(m->pParent, DeviceType_DVD, Bstr(rawDevName));
3494 list.push_back(hostDVDDriveObj);
3495 }
3496 free(rawDevName);
3497 }
3498 }
3499 }
3500
3501 fclose(mntFile);
3502 }
3503#endif
3504}
3505
3506/**
3507 * Helper function to check whether the given device node is a valid drive
3508 */
3509bool Host::i_validateDevice(const char *deviceNode, bool isCDROM)
3510{
3511 struct stat statInfo;
3512 bool retValue = false;
3513
3514 // sanity check
3515 if (!deviceNode)
3516 {
3517 return false;
3518 }
3519
3520 // first a simple stat() call
3521 if (stat(deviceNode, &statInfo) < 0)
3522 {
3523 return false;
3524 }
3525 else
3526 {
3527 if (isCDROM)
3528 {
3529 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
3530 {
3531 int fileHandle;
3532 // now try to open the device
3533 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
3534 if (fileHandle >= 0)
3535 {
3536 cdrom_subchnl cdChannelInfo;
3537 cdChannelInfo.cdsc_format = CDROM_MSF;
3538 // this call will finally reveal the whole truth
3539#ifdef RT_OS_LINUX
3540 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
3541 (errno == EIO) || (errno == ENOENT) ||
3542 (errno == EINVAL) || (errno == ENOMEDIUM))
3543#else
3544 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
3545 (errno == EIO) || (errno == ENOENT) ||
3546 (errno == EINVAL))
3547#endif
3548 {
3549 retValue = true;
3550 }
3551 close(fileHandle);
3552 }
3553 }
3554 } else
3555 {
3556 // floppy case
3557 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
3558 {
3559 /// @todo do some more testing, maybe a nice IOCTL!
3560 retValue = true;
3561 }
3562 }
3563 }
3564 return retValue;
3565}
3566#endif // RT_OS_SOLARIS
3567
3568#ifdef VBOX_WITH_USB
3569/**
3570 * Checks for the presence and status of the USB Proxy Service.
3571 * Returns S_OK when the Proxy is present and OK, VBOX_E_HOST_ERROR (as a
3572 * warning) if the proxy service is not available due to the way the host is
3573 * configured (at present, that means that usbfs and hal/DBus are not
3574 * available on a Linux host) or E_FAIL and a corresponding error message
3575 * otherwise. Intended to be used by methods that rely on the Proxy Service
3576 * availability.
3577 *
3578 * @note This method may return a warning result code. It is recommended to use
3579 * MultiError to store the return value.
3580 *
3581 * @note Locks this object for reading.
3582 */
3583HRESULT Host::i_checkUSBProxyService()
3584{
3585 AutoCaller autoCaller(this);
3586 if (FAILED(autoCaller.rc()))
3587 return autoCaller.rc();
3588
3589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3590
3591 AssertReturn(m->pUSBProxyService, E_FAIL);
3592 if (!m->pUSBProxyService->isActive())
3593 {
3594 /* disable the USB controller completely to avoid assertions if the
3595 * USB proxy service could not start. */
3596
3597 switch (m->pUSBProxyService->getLastError())
3598 {
3599 case VERR_FILE_NOT_FOUND: /** @todo what does this mean? */
3600 return setWarning(E_FAIL,
3601 tr("Could not load the Host USB Proxy Service (VERR_FILE_NOT_FOUND). The service might not be installed on the host computer"));
3602 case VERR_VUSB_USB_DEVICE_PERMISSION:
3603 return setWarning(E_FAIL,
3604 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"));
3605 case VERR_VUSB_USBFS_PERMISSION:
3606 return setWarning(E_FAIL,
3607 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"));
3608 case VINF_SUCCESS:
3609 return setWarning(E_FAIL,
3610 tr("The USB Proxy Service has not yet been ported to this host"));
3611 default:
3612 return setWarning(E_FAIL, "%s: %Rrc",
3613 tr("Could not load the Host USB Proxy service"),
3614 m->pUSBProxyService->getLastError());
3615 }
3616 }
3617
3618 return S_OK;
3619}
3620#endif /* VBOX_WITH_USB */
3621
3622HRESULT Host::i_updateNetIfList()
3623{
3624#ifdef VBOX_WITH_HOSTNETIF_API
3625 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
3626
3627 /** @todo r=klaus it would save lots of clock cycles if for concurrent
3628 * threads executing this code we'd only do one interface enumeration
3629 * and update, and let the other threads use the result as is. However
3630 * if there's a constant hammering of this method, we don't want this
3631 * to cause update starvation. */
3632 HostNetworkInterfaceList list;
3633 int rc = NetIfList(list);
3634 if (rc)
3635 {
3636 Log(("Failed to get host network interface list with rc=%Rrc\n", rc));
3637 return E_FAIL;
3638 }
3639
3640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3641
3642 AssertReturn(m->pParent, E_FAIL);
3643 /* Make a copy as the original may be partially destroyed later. */
3644 HostNetworkInterfaceList listCopy(list);
3645 HostNetworkInterfaceList::iterator itOld, itNew;
3646# ifdef VBOX_WITH_RESOURCE_USAGE_API
3647 PerformanceCollector *aCollector = m->pParent->i_performanceCollector();
3648# endif
3649 for (itOld = m->llNetIfs.begin(); itOld != m->llNetIfs.end(); ++itOld)
3650 {
3651 bool fGone = true;
3652 Bstr nameOld;
3653 (*itOld)->COMGETTER(Name)(nameOld.asOutParam());
3654 for (itNew = listCopy.begin(); itNew != listCopy.end(); ++itNew)
3655 {
3656 Bstr nameNew;
3657 (*itNew)->COMGETTER(Name)(nameNew.asOutParam());
3658 if (nameNew == nameOld)
3659 {
3660 fGone = false;
3661 (*itNew)->uninit();
3662 listCopy.erase(itNew);
3663 break;
3664 }
3665 }
3666 if (fGone)
3667 {
3668# ifdef VBOX_WITH_RESOURCE_USAGE_API
3669 (*itOld)->i_unregisterMetrics(aCollector, this);
3670 (*itOld)->uninit();
3671# endif
3672 }
3673 }
3674 /*
3675 * Need to set the references to VirtualBox object in all interface objects
3676 * (see @bugref{6439}).
3677 */
3678 for (itNew = list.begin(); itNew != list.end(); ++itNew)
3679 (*itNew)->i_setVirtualBox(m->pParent);
3680 /* At this point listCopy will contain newly discovered interfaces only. */
3681 for (itNew = listCopy.begin(); itNew != listCopy.end(); ++itNew)
3682 {
3683 HostNetworkInterfaceType_T t;
3684 HRESULT hrc = (*itNew)->COMGETTER(InterfaceType)(&t);
3685 if (FAILED(hrc))
3686 {
3687 Bstr n;
3688 (*itNew)->COMGETTER(Name)(n.asOutParam());
3689 LogRel(("Host::updateNetIfList: failed to get interface type for %ls\n", n.raw()));
3690 }
3691 else if (t == HostNetworkInterfaceType_Bridged)
3692 {
3693# ifdef VBOX_WITH_RESOURCE_USAGE_API
3694 (*itNew)->i_registerMetrics(aCollector, this);
3695# endif
3696 }
3697 }
3698 m->llNetIfs = list;
3699 return S_OK;
3700#else
3701 return E_NOTIMPL;
3702#endif
3703}
3704
3705#ifdef VBOX_WITH_RESOURCE_USAGE_API
3706
3707void Host::i_registerDiskMetrics(PerformanceCollector *aCollector)
3708{
3709 pm::CollectorHAL *hal = aCollector->getHAL();
3710 /* Create sub metrics */
3711 Utf8StrFmt fsNameBase("FS/{%s}/Usage", "/");
3712 //Utf8StrFmt fsNameBase("Filesystem/[root]/Usage");
3713 pm::SubMetric *fsRootUsageTotal = new pm::SubMetric(fsNameBase + "/Total",
3714 "Root file system size.");
3715 pm::SubMetric *fsRootUsageUsed = new pm::SubMetric(fsNameBase + "/Used",
3716 "Root file system space currently occupied.");
3717 pm::SubMetric *fsRootUsageFree = new pm::SubMetric(fsNameBase + "/Free",
3718 "Root file system space currently empty.");
3719
3720 pm::BaseMetric *fsRootUsage = new pm::HostFilesystemUsage(hal, this,
3721 fsNameBase, "/",
3722 fsRootUsageTotal,
3723 fsRootUsageUsed,
3724 fsRootUsageFree);
3725 aCollector->registerBaseMetric(fsRootUsage);
3726
3727 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageTotal, 0));
3728 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageTotal,
3729 new pm::AggregateAvg()));
3730 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageTotal,
3731 new pm::AggregateMin()));
3732 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageTotal,
3733 new pm::AggregateMax()));
3734
3735 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageUsed, 0));
3736 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageUsed,
3737 new pm::AggregateAvg()));
3738 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageUsed,
3739 new pm::AggregateMin()));
3740 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageUsed,
3741 new pm::AggregateMax()));
3742
3743 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageFree, 0));
3744 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageFree,
3745 new pm::AggregateAvg()));
3746 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageFree,
3747 new pm::AggregateMin()));
3748 aCollector->registerMetric(new pm::Metric(fsRootUsage, fsRootUsageFree,
3749 new pm::AggregateMax()));
3750
3751 /* For now we are concerned with the root file system only. */
3752 pm::DiskList disksUsage, disksLoad;
3753 int rc = hal->getDiskListByFs("/", disksUsage, disksLoad);
3754 if (RT_FAILURE(rc))
3755 return;
3756 pm::DiskList::iterator it;
3757 for (it = disksLoad.begin(); it != disksLoad.end(); ++it)
3758 {
3759 Utf8StrFmt strName("Disk/%s", it->c_str());
3760 pm::SubMetric *fsLoadUtil = new pm::SubMetric(strName + "/Load/Util",
3761 "Percentage of time disk was busy serving I/O requests.");
3762 pm::BaseMetric *fsLoad = new pm::HostDiskLoadRaw(hal, this, strName + "/Load",
3763 *it, fsLoadUtil);
3764 aCollector->registerBaseMetric(fsLoad);
3765
3766 aCollector->registerMetric(new pm::Metric(fsLoad, fsLoadUtil, 0));
3767 aCollector->registerMetric(new pm::Metric(fsLoad, fsLoadUtil,
3768 new pm::AggregateAvg()));
3769 aCollector->registerMetric(new pm::Metric(fsLoad, fsLoadUtil,
3770 new pm::AggregateMin()));
3771 aCollector->registerMetric(new pm::Metric(fsLoad, fsLoadUtil,
3772 new pm::AggregateMax()));
3773 }
3774 for (it = disksUsage.begin(); it != disksUsage.end(); ++it)
3775 {
3776 Utf8StrFmt strName("Disk/%s", it->c_str());
3777 pm::SubMetric *fsUsageTotal = new pm::SubMetric(strName + "/Usage/Total",
3778 "Disk size.");
3779 pm::BaseMetric *fsUsage = new pm::HostDiskUsage(hal, this, strName + "/Usage",
3780 *it, fsUsageTotal);
3781 aCollector->registerBaseMetric(fsUsage);
3782
3783 aCollector->registerMetric(new pm::Metric(fsUsage, fsUsageTotal, 0));
3784 aCollector->registerMetric(new pm::Metric(fsUsage, fsUsageTotal,
3785 new pm::AggregateAvg()));
3786 aCollector->registerMetric(new pm::Metric(fsUsage, fsUsageTotal,
3787 new pm::AggregateMin()));
3788 aCollector->registerMetric(new pm::Metric(fsUsage, fsUsageTotal,
3789 new pm::AggregateMax()));
3790 }
3791}
3792
3793void Host::i_registerMetrics(PerformanceCollector *aCollector)
3794{
3795 pm::CollectorHAL *hal = aCollector->getHAL();
3796 /* Create sub metrics */
3797 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
3798 "Percentage of processor time spent in user mode.");
3799 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
3800 "Percentage of processor time spent in kernel mode.");
3801 pm::SubMetric *cpuLoadIdle = new pm::SubMetric("CPU/Load/Idle",
3802 "Percentage of processor time spent idling.");
3803 pm::SubMetric *cpuMhzSM = new pm::SubMetric("CPU/MHz",
3804 "Average of current frequency of all processors.");
3805 pm::SubMetric *ramUsageTotal = new pm::SubMetric("RAM/Usage/Total",
3806 "Total physical memory installed.");
3807 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
3808 "Physical memory currently occupied.");
3809 pm::SubMetric *ramUsageFree = new pm::SubMetric("RAM/Usage/Free",
3810 "Physical memory currently available to applications.");
3811 pm::SubMetric *ramVMMUsed = new pm::SubMetric("RAM/VMM/Used",
3812 "Total physical memory used by the hypervisor.");
3813 pm::SubMetric *ramVMMFree = new pm::SubMetric("RAM/VMM/Free",
3814 "Total physical memory free inside the hypervisor.");
3815 pm::SubMetric *ramVMMBallooned = new pm::SubMetric("RAM/VMM/Ballooned",
3816 "Total physical memory ballooned by the hypervisor.");
3817 pm::SubMetric *ramVMMShared = new pm::SubMetric("RAM/VMM/Shared",
3818 "Total physical memory shared between VMs.");
3819
3820
3821 /* Create and register base metrics */
3822 pm::BaseMetric *cpuLoad = new pm::HostCpuLoadRaw(hal, this, cpuLoadUser, cpuLoadKernel,
3823 cpuLoadIdle);
3824 aCollector->registerBaseMetric(cpuLoad);
3825 pm::BaseMetric *cpuMhz = new pm::HostCpuMhz(hal, this, cpuMhzSM);
3826 aCollector->registerBaseMetric(cpuMhz);
3827 pm::BaseMetric *ramUsage = new pm::HostRamUsage(hal, this,
3828 ramUsageTotal,
3829 ramUsageUsed,
3830 ramUsageFree);
3831 aCollector->registerBaseMetric(ramUsage);
3832 pm::BaseMetric *ramVmm = new pm::HostRamVmm(aCollector->getGuestManager(), this,
3833 ramVMMUsed,
3834 ramVMMFree,
3835 ramVMMBallooned,
3836 ramVMMShared);
3837 aCollector->registerBaseMetric(ramVmm);
3838
3839 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
3840 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
3841 new pm::AggregateAvg()));
3842 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
3843 new pm::AggregateMin()));
3844 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
3845 new pm::AggregateMax()));
3846
3847 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
3848 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
3849 new pm::AggregateAvg()));
3850 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
3851 new pm::AggregateMin()));
3852 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
3853 new pm::AggregateMax()));
3854
3855 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadIdle, 0));
3856 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadIdle,
3857 new pm::AggregateAvg()));
3858 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadIdle,
3859 new pm::AggregateMin()));
3860 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadIdle,
3861 new pm::AggregateMax()));
3862
3863 aCollector->registerMetric(new pm::Metric(cpuMhz, cpuMhzSM, 0));
3864 aCollector->registerMetric(new pm::Metric(cpuMhz, cpuMhzSM,
3865 new pm::AggregateAvg()));
3866 aCollector->registerMetric(new pm::Metric(cpuMhz, cpuMhzSM,
3867 new pm::AggregateMin()));
3868 aCollector->registerMetric(new pm::Metric(cpuMhz, cpuMhzSM,
3869 new pm::AggregateMax()));
3870
3871 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageTotal, 0));
3872 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageTotal,
3873 new pm::AggregateAvg()));
3874 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageTotal,
3875 new pm::AggregateMin()));
3876 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageTotal,
3877 new pm::AggregateMax()));
3878
3879 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
3880 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
3881 new pm::AggregateAvg()));
3882 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
3883 new pm::AggregateMin()));
3884 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
3885 new pm::AggregateMax()));
3886
3887 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageFree, 0));
3888 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageFree,
3889 new pm::AggregateAvg()));
3890 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageFree,
3891 new pm::AggregateMin()));
3892 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageFree,
3893 new pm::AggregateMax()));
3894
3895 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMUsed, 0));
3896 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMUsed,
3897 new pm::AggregateAvg()));
3898 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMUsed,
3899 new pm::AggregateMin()));
3900 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMUsed,
3901 new pm::AggregateMax()));
3902
3903 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMFree, 0));
3904 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMFree,
3905 new pm::AggregateAvg()));
3906 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMFree,
3907 new pm::AggregateMin()));
3908 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMFree,
3909 new pm::AggregateMax()));
3910
3911 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMBallooned, 0));
3912 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMBallooned,
3913 new pm::AggregateAvg()));
3914 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMBallooned,
3915 new pm::AggregateMin()));
3916 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMBallooned,
3917 new pm::AggregateMax()));
3918
3919 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMShared, 0));
3920 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMShared,
3921 new pm::AggregateAvg()));
3922 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMShared,
3923 new pm::AggregateMin()));
3924 aCollector->registerMetric(new pm::Metric(ramVmm, ramVMMShared,
3925 new pm::AggregateMax()));
3926 i_registerDiskMetrics(aCollector);
3927}
3928
3929void Host::i_unregisterMetrics(PerformanceCollector *aCollector)
3930{
3931 aCollector->unregisterMetricsFor(this);
3932 aCollector->unregisterBaseMetricsFor(this);
3933}
3934
3935#endif /* VBOX_WITH_RESOURCE_USAGE_API */
3936
3937
3938/* static */
3939void Host::i_generateMACAddress(Utf8Str &mac)
3940{
3941 /*
3942 * Our strategy is as follows: the first three bytes are our fixed
3943 * vendor ID (080027). The remaining 3 bytes will be taken from the
3944 * start of a GUID. This is a fairly safe algorithm.
3945 */
3946 Guid guid;
3947 guid.create();
3948 mac = Utf8StrFmt("080027%02X%02X%02X",
3949 guid.raw()->au8[0], guid.raw()->au8[1], guid.raw()->au8[2]);
3950}
3951
3952#ifdef RT_OS_WINDOWS
3953HRESULT Host::i_getFixedDrivesFromGlobalNamespace(std::list<std::pair<com::Utf8Str, com::Utf8Str> > &aDriveList) RT_NOEXCEPT
3954{
3955 RTERRINFOSTATIC ErrInfo;
3956 uint32_t offError;
3957 RTVFSDIR hVfsDir;
3958 int rc = RTVfsChainOpenDir("\\\\:iprtnt:\\GLOBAL??", 0 /*fFlags*/, &hVfsDir, &offError, RTErrInfoInitStatic(&ErrInfo));
3959 if (RT_FAILURE(rc))
3960 return setError(E_FAIL, tr("Failed to open NT\\GLOBAL?? (error %Rrc)"), rc);
3961
3962 /*
3963 * Scan whole directory and find any 'PhysicalDiskX' entries. Next, combine with '\\.\'
3964 * to obtain the harddisk dev path.
3965 */
3966 size_t cbDirEntryAlloced = sizeof(RTDIRENTRYEX);
3967 PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
3968 if (!pDirEntry)
3969 {
3970 RTVfsDirRelease(hVfsDir);
3971 return setError(E_OUTOFMEMORY, tr("Out of memory! (direntry buffer)"));
3972 }
3973
3974 HRESULT hrc = S_OK;
3975 for (;;)
3976 {
3977 size_t cbDirEntry = cbDirEntryAlloced;
3978 rc = RTVfsDirReadEx(hVfsDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING);
3979 if (RT_FAILURE(rc))
3980 {
3981 if (rc == VERR_BUFFER_OVERFLOW)
3982 {
3983 RTMemTmpFree(pDirEntry);
3984 cbDirEntryAlloced = RT_ALIGN_Z(RT_MIN(cbDirEntry, cbDirEntryAlloced) + 64, 64);
3985 pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
3986 if (pDirEntry)
3987 continue;
3988 hrc = setError(E_OUTOFMEMORY, tr("Out of memory! (direntry buffer)"));
3989 }
3990 else if (rc != VERR_NO_MORE_FILES)
3991 hrc = setError(VBOX_E_IPRT_ERROR, tr("RTVfsDirReadEx failed: %Rrc"), rc);
3992 break;
3993 }
3994 if (RTStrStartsWith(pDirEntry->szName, "PhysicalDrive"))
3995 {
3996 char szPhysicalDrive[64];
3997 RTStrPrintf(szPhysicalDrive, sizeof(szPhysicalDrive), "\\\\.\\%s", pDirEntry->szName);
3998
3999 RTFILE hRawFile = NIL_RTFILE;
4000 int vrc = RTFileOpen(&hRawFile, szPhysicalDrive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
4001 if (RT_FAILURE(vrc))
4002 {
4003 try
4004 {
4005 aDriveList.push_back(std::pair<com::Utf8Str, com::Utf8Str>(szPhysicalDrive, tr("Unknown (Access denied)")));
4006 }
4007 catch (std::bad_alloc &)
4008 {
4009 hrc = setError(E_OUTOFMEMORY, tr("Out of memory"));
4010 break;
4011 }
4012 continue;
4013 }
4014
4015 DWORD cbBytesReturned = 0;
4016 uint8_t abBuffer[1024];
4017 RT_ZERO(abBuffer);
4018
4019 STORAGE_PROPERTY_QUERY query;
4020 RT_ZERO(query);
4021 query.PropertyId = StorageDeviceProperty;
4022 query.QueryType = PropertyStandardQuery;
4023
4024 BOOL fRc = DeviceIoControl((HANDLE)RTFileToNative(hRawFile),
4025 IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query),
4026 abBuffer, sizeof(abBuffer), &cbBytesReturned, NULL);
4027 RTFileClose(hRawFile);
4028 char szModel[1024];
4029 if (fRc)
4030 {
4031 PSTORAGE_DEVICE_DESCRIPTOR pDevDescriptor = (PSTORAGE_DEVICE_DESCRIPTOR)abBuffer;
4032 char *pszProduct = pDevDescriptor->ProductIdOffset ? (char *)&abBuffer[pDevDescriptor->ProductIdOffset] : NULL;
4033 if (pszProduct)
4034 {
4035 RTStrPurgeEncoding(pszProduct);
4036 if (*pszProduct != '\0')
4037 {
4038 char *pszVendor = pDevDescriptor->VendorIdOffset ? (char *)&abBuffer[pDevDescriptor->VendorIdOffset] : NULL;
4039 if (pszVendor)
4040 RTStrPurgeEncoding(pszVendor);
4041 if (pszVendor && *pszVendor)
4042 RTStrPrintf(szModel, sizeof(szModel), "%s %s", pszVendor, pszProduct);
4043 else
4044 RTStrCopy(szModel, sizeof(szModel), pszProduct);
4045 }
4046 }
4047 }
4048 try
4049 {
4050 aDriveList.push_back(std::pair<com::Utf8Str, com::Utf8Str>(szPhysicalDrive, szModel));
4051 }
4052 catch (std::bad_alloc &)
4053 {
4054 hrc = setError(E_OUTOFMEMORY, tr("Out of memory"));
4055 break;
4056 }
4057 }
4058 }
4059 if (FAILED(hrc))
4060 aDriveList.clear();
4061 RTMemTmpFree(pDirEntry);
4062 RTVfsDirRelease(hVfsDir);
4063 return hrc;
4064}
4065#endif
4066
4067/**
4068 * @throws nothing
4069 */
4070HRESULT Host::i_getDrivesPathsList(std::list<std::pair<com::Utf8Str, com::Utf8Str> > &aDriveList) RT_NOEXCEPT
4071{
4072#ifdef RT_OS_WINDOWS
4073 return i_getFixedDrivesFromGlobalNamespace(aDriveList);
4074
4075#elif defined(RT_OS_DARWIN)
4076 /*
4077 * Get the list of fixed drives from iokit.cpp and transfer it to aDriveList.
4078 */
4079 PDARWINFIXEDDRIVE pDrives = DarwinGetFixedDrives();
4080 HRESULT hrc;
4081 try
4082 {
4083 for (PDARWINFIXEDDRIVE pCurDrv = pDrives; pCurDrv; pCurDrv = pCurDrv->pNext)
4084 aDriveList.push_back(std::pair<com::Utf8Str, com::Utf8Str>(pCurDrv->szName, pCurDrv->pszModel));
4085 hrc = S_OK;
4086 }
4087 catch (std::bad_alloc &)
4088 {
4089 aDriveList.clear();
4090 hrc = E_OUTOFMEMORY;
4091 }
4092
4093 while (pDrives)
4094 {
4095 PDARWINFIXEDDRIVE pFreeMe = pDrives;
4096 pDrives = pDrives->pNext;
4097 ASMCompilerBarrier();
4098 RTMemFree(pFreeMe);
4099 }
4100 return hrc;
4101
4102#elif defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
4103 /*
4104 * The list of fixed drives is kept in the VBoxMainDriveInfo instance, so
4105 * update it and tranfer the info to aDriveList.
4106 *
4107 * This obviously requires us to write lock the object!
4108 */
4109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4110 int vrc = m->hostDrives.updateFixedDrives(); /* nothrow */
4111 if (RT_FAILURE(vrc))
4112 return setErrorBoth(E_FAIL, vrc, tr("Failed to update fixed drive list (%Rrc)"), vrc);
4113
4114 try
4115 {
4116 for (DriveInfoList::const_iterator it = m->hostDrives.FixedDriveBegin(); it != m->hostDrives.FixedDriveEnd(); ++it)
4117 aDriveList.push_back(std::pair<com::Utf8Str, com::Utf8Str>(it->mDevice, it->mDescription));
4118 }
4119 catch (std::bad_alloc &)
4120 {
4121 aDriveList.clear();
4122 return E_OUTOFMEMORY;
4123 }
4124 return S_OK;
4125
4126#elif defined(RT_OS_SOLARIS)
4127 /*
4128 * We can get the info from HAL, if not present/working we'll get by
4129 * walking the device tree.
4130 */
4131# ifdef VBOX_USE_LIBHAL
4132 HRESULT hrc = i_getFixedDrivesFromHal(aDriveList);
4133 if (hrc != S_FALSE)
4134 return hrc;
4135 aDriveList.clear(); /* just in case */
4136# endif
4137 return i_getFixedDrivesFromDevTree(aDriveList);
4138
4139#else
4140 /* PORTME */
4141 RT_NOREF(aDriveList);
4142 return E_NOTIMPL;
4143#endif
4144}
4145
4146/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette