VirtualBox

source: vbox/trunk/src/VBox/Main/HostImpl.cpp@ 8861

Last change on this file since 8861 was 8766, checked in by vboxsync, 17 years ago

USBProxyServiceWin32 -> USBProxyServiceWindows

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 90.8 KB
Line 
1/* $Id: HostImpl.cpp 8766 2008-05-12 01:03:38Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Host
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#ifdef RT_OS_LINUX
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <unistd.h>
26#include <sys/ioctl.h>
27#include <fcntl.h>
28#include <mntent.h>
29/* bird: This is a hack to work around conflicts between these linux kernel headers
30 * and the GLIBC tcpip headers. They have different declarations of the 4
31 * standard byte order functions. */
32#define _LINUX_BYTEORDER_GENERIC_H
33#include <linux/cdrom.h>
34#ifdef VBOX_USE_LIBHAL
35// # include <libhal.h>
36// /* These are defined by libhal.h and by VBox header files. */
37// # undef TRUE
38// # undef FALSE
39#include "vbox-libhal.h"
40#endif
41#include <errno.h>
42#endif /* RT_OS_LINUX */
43
44#ifdef RT_OS_SOLARIS
45# include <fcntl.h>
46# include <unistd.h>
47# include <stropts.h>
48# include <errno.h>
49# include <limits.h>
50# include <stdio.h>
51# include <sys/types.h>
52# include <sys/stat.h>
53# include <sys/cdio.h>
54# include <sys/dkio.h>
55# include <sys/mnttab.h>
56# include <sys/mntent.h>
57# ifdef VBOX_USE_LIBHAL
58# include "vbox-libhal.h"
59extern "C" char *getfullrawname(char *);
60# endif
61#endif /* RT_OS_SOLARIS */
62
63#ifdef RT_OS_WINDOWS
64#define _WIN32_DCOM
65#include <windows.h>
66#include <shellapi.h>
67#define INITGUID
68#include <guiddef.h>
69#include <devguid.h>
70#include <objbase.h>
71#include <setupapi.h>
72#include <shlobj.h>
73#include <cfgmgr32.h>
74#endif /* RT_OS_WINDOWS */
75
76
77#include "HostImpl.h"
78#include "HostDVDDriveImpl.h"
79#include "HostFloppyDriveImpl.h"
80#ifdef VBOX_WITH_USB
81# include "HostUSBDeviceImpl.h"
82# include "USBDeviceFilterImpl.h"
83# include "USBProxyService.h"
84#endif
85#include "VirtualBoxImpl.h"
86#include "MachineImpl.h"
87#include "Logging.h"
88
89#ifdef RT_OS_DARWIN
90# include "darwin/iokit.h"
91#endif
92
93#ifdef RT_OS_WINDOWS
94# include "HostNetworkInterfaceImpl.h"
95#endif
96
97#include <VBox/usb.h>
98#include <VBox/err.h>
99#include <iprt/string.h>
100#include <iprt/system.h>
101#include <iprt/time.h>
102#include <iprt/param.h>
103#include <iprt/env.h>
104#ifdef RT_OS_SOLARIS
105# include <iprt/path.h>
106#endif
107
108#include <stdio.h>
109
110#include <algorithm>
111
112// constructor / destructor
113/////////////////////////////////////////////////////////////////////////////
114
115HRESULT Host::FinalConstruct()
116{
117 return S_OK;
118}
119
120void Host::FinalRelease()
121{
122 if (isReady())
123 uninit();
124}
125
126// public initializer/uninitializer for internal purposes only
127/////////////////////////////////////////////////////////////////////////////
128
129/**
130 * Initializes the host object.
131 *
132 * @returns COM result indicator
133 * @param parent handle of our parent object
134 */
135HRESULT Host::init (VirtualBox *parent)
136{
137 LogFlowThisFunc (("isReady=%d\n", isReady()));
138
139 ComAssertRet (parent, E_INVALIDARG);
140
141 AutoWriteLock alock (this);
142 ComAssertRet (!isReady(), E_UNEXPECTED);
143
144 mParent = parent;
145
146#ifdef VBOX_WITH_USB
147 /*
148 * Create and initialize the USB Proxy Service.
149 */
150# if defined (RT_OS_DARWIN)
151 mUSBProxyService = new USBProxyServiceDarwin (this);
152# elif defined (RT_OS_LINUX)
153 mUSBProxyService = new USBProxyServiceLinux (this);
154# elif defined (RT_OS_OS2)
155 mUSBProxyService = new USBProxyServiceOs2 (this);
156# elif defined (RT_OS_SOLARIS) && 0
157 mUSBProxyService = new USBProxyServiceSolaris (this);
158# elif defined (RT_OS_WINDOWS)
159 mUSBProxyService = new USBProxyServiceWindows (this);
160# else
161 mUSBProxyService = new USBProxyService (this);
162# endif
163 HRESULT hrc = mUSBProxyService->init();
164 AssertComRCReturn(hrc, hrc);
165#endif /* VBOX_WITH_USB */
166
167 setReady(true);
168 return S_OK;
169}
170
171/**
172 * Uninitializes the host object and sets the ready flag to FALSE.
173 * Called either from FinalRelease() or by the parent when it gets destroyed.
174 */
175void Host::uninit()
176{
177 LogFlowThisFunc (("isReady=%d\n", isReady()));
178
179 AssertReturn (isReady(), (void) 0);
180
181#ifdef VBOX_WITH_USB
182 /* wait for USB proxy service to terminate before we uninit all USB
183 * devices */
184 LogFlowThisFunc (("Stopping USB proxy service...\n"));
185 delete mUSBProxyService;
186 mUSBProxyService = NULL;
187 LogFlowThisFunc (("Done stopping USB proxy service.\n"));
188#endif
189
190 /* uninit all USB device filters still referenced by clients */
191 uninitDependentChildren();
192
193#ifdef VBOX_WITH_USB
194 mUSBDeviceFilters.clear();
195#endif
196
197 setReady (FALSE);
198}
199
200// IHost properties
201/////////////////////////////////////////////////////////////////////////////
202
203/**
204 * Returns a list of host DVD drives.
205 *
206 * @returns COM status code
207 * @param drives address of result pointer
208 */
209STDMETHODIMP Host::COMGETTER(DVDDrives) (IHostDVDDriveCollection **drives)
210{
211 if (!drives)
212 return E_POINTER;
213 AutoWriteLock alock (this);
214 CHECK_READY();
215 std::list <ComObjPtr <HostDVDDrive> > list;
216
217#if defined(RT_OS_WINDOWS)
218 int sz = GetLogicalDriveStrings(0, NULL);
219 TCHAR *hostDrives = new TCHAR[sz+1];
220 GetLogicalDriveStrings(sz, hostDrives);
221 wchar_t driveName[3] = { '?', ':', '\0' };
222 TCHAR *p = hostDrives;
223 do
224 {
225 if (GetDriveType(p) == DRIVE_CDROM)
226 {
227 driveName[0] = *p;
228 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
229 hostDVDDriveObj.createObject();
230 hostDVDDriveObj->init (Bstr (driveName));
231 list.push_back (hostDVDDriveObj);
232 }
233 p += _tcslen(p) + 1;
234 }
235 while (*p);
236 delete[] hostDrives;
237
238#elif defined(RT_OS_SOLARIS)
239# ifdef VBOX_USE_LIBHAL
240 if (!getDVDInfoFromHal(list))
241# endif
242 // Not all Solaris versions ship with libhal.
243 // So use a fallback approach similar to Linux.
244 {
245 if (RTEnvGet("VBOX_CDROM"))
246 {
247 char *cdromEnv = strdup(RTEnvGet("VBOX_CDROM"));
248 char *cdromDrive;
249 cdromDrive = strtok(cdromEnv, ":"); /** @todo use strtok_r. */
250 while (cdromDrive)
251 {
252 if (validateDevice(cdromDrive, true))
253 {
254 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
255 hostDVDDriveObj.createObject();
256 hostDVDDriveObj->init (Bstr (cdromDrive));
257 list.push_back (hostDVDDriveObj);
258 }
259 cdromDrive = strtok(NULL, ":");
260 }
261 free(cdromEnv);
262 }
263 else
264 {
265 // this might work on Solaris version older than Nevada.
266 if (validateDevice("/cdrom/cdrom0", true))
267 {
268 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
269 hostDVDDriveObj.createObject();
270 hostDVDDriveObj->init (Bstr ("cdrom/cdrom0"));
271 list.push_back (hostDVDDriveObj);
272 }
273
274 // check the mounted drives
275 parseMountTable(MNTTAB, list);
276 }
277 }
278
279#elif defined(RT_OS_LINUX)
280#ifdef VBOX_USE_LIBHAL
281 if (!getDVDInfoFromHal(list)) /* Playing with #defines in this way is nasty, I know. */
282#endif /* USE_LIBHAL defined */
283 // On Linux without hal, the situation is much more complex. We will take a
284 // heuristical approach and also allow the user to specify a list of host
285 // CDROMs using an environment variable.
286 // The general strategy is to try some known device names and see of they
287 // exist. At last, we'll enumerate the /etc/fstab file (luckily there's an
288 // API to parse it) for CDROM devices. Ok, let's start!
289
290 {
291 if (RTEnvGet("VBOX_CDROM"))
292 {
293 char *cdromEnv = strdupa(RTEnvGet("VBOX_CDROM"));
294 char *cdromDrive;
295 cdromDrive = strtok(cdromEnv, ":"); /** @todo use strtok_r */
296 while (cdromDrive)
297 {
298 if (validateDevice(cdromDrive, true))
299 {
300 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
301 hostDVDDriveObj.createObject();
302 hostDVDDriveObj->init (Bstr (cdromDrive));
303 list.push_back (hostDVDDriveObj);
304 }
305 cdromDrive = strtok(NULL, ":");
306 }
307 }
308 else
309 {
310 // this is a good guess usually
311 if (validateDevice("/dev/cdrom", true))
312 {
313 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
314 hostDVDDriveObj.createObject();
315 hostDVDDriveObj->init (Bstr ("/dev/cdrom"));
316 list.push_back (hostDVDDriveObj);
317 }
318
319 // check the mounted drives
320 parseMountTable((char*)"/etc/mtab", list);
321
322 // check the drives that can be mounted
323 parseMountTable((char*)"/etc/fstab", list);
324 }
325 }
326#elif defined(RT_OS_DARWIN)
327 PDARWINDVD cur = DarwinGetDVDDrives();
328 while (cur)
329 {
330 ComObjPtr<HostDVDDrive> hostDVDDriveObj;
331 hostDVDDriveObj.createObject();
332 hostDVDDriveObj->init(Bstr(cur->szName));
333 list.push_back(hostDVDDriveObj);
334
335 /* next */
336 void *freeMe = cur;
337 cur = cur->pNext;
338 RTMemFree(freeMe);
339 }
340
341#else
342 /* PORTME */
343#endif
344
345 ComObjPtr<HostDVDDriveCollection> collection;
346 collection.createObject();
347 collection->init (list);
348 collection.queryInterfaceTo(drives);
349 return S_OK;
350}
351
352/**
353 * Returns a list of host floppy drives.
354 *
355 * @returns COM status code
356 * @param drives address of result pointer
357 */
358STDMETHODIMP Host::COMGETTER(FloppyDrives) (IHostFloppyDriveCollection **drives)
359{
360 if (!drives)
361 return E_POINTER;
362 AutoWriteLock alock (this);
363 CHECK_READY();
364
365 std::list <ComObjPtr <HostFloppyDrive> > list;
366
367#ifdef RT_OS_WINDOWS
368 int sz = GetLogicalDriveStrings(0, NULL);
369 TCHAR *hostDrives = new TCHAR[sz+1];
370 GetLogicalDriveStrings(sz, hostDrives);
371 wchar_t driveName[3] = { '?', ':', '\0' };
372 TCHAR *p = hostDrives;
373 do
374 {
375 if (GetDriveType(p) == DRIVE_REMOVABLE)
376 {
377 driveName[0] = *p;
378 ComObjPtr <HostFloppyDrive> hostFloppyDriveObj;
379 hostFloppyDriveObj.createObject();
380 hostFloppyDriveObj->init (Bstr (driveName));
381 list.push_back (hostFloppyDriveObj);
382 }
383 p += _tcslen(p) + 1;
384 }
385 while (*p);
386 delete[] hostDrives;
387#elif defined(RT_OS_LINUX)
388#ifdef VBOX_USE_LIBHAL
389 if (!getFloppyInfoFromHal(list)) /* Playing with #defines in this way is nasty, I know. */
390#endif /* USE_LIBHAL defined */
391 // As with the CDROMs, on Linux we have to take a multi-level approach
392 // involving parsing the mount tables. As this is not bulletproof, we'll
393 // give the user the chance to override the detection by an environment
394 // variable and skip the detection.
395
396 {
397 if (RTEnvGet("VBOX_FLOPPY"))
398 {
399 char *floppyEnv = strdupa(RTEnvGet("VBOX_FLOPPY"));
400 char *floppyDrive;
401 floppyDrive = strtok(floppyEnv, ":");
402 while (floppyDrive)
403 {
404 // check if this is an acceptable device
405 if (validateDevice(floppyDrive, false))
406 {
407 ComObjPtr <HostFloppyDrive> hostFloppyDriveObj;
408 hostFloppyDriveObj.createObject();
409 hostFloppyDriveObj->init (Bstr (floppyDrive));
410 list.push_back (hostFloppyDriveObj);
411 }
412 floppyDrive = strtok(NULL, ":");
413 }
414 }
415 else
416 {
417 // we assume that a floppy is always /dev/fd[x] with x from 0 to 7
418 char devName[10];
419 for (int i = 0; i <= 7; i++)
420 {
421 sprintf(devName, "/dev/fd%d", i);
422 if (validateDevice(devName, false))
423 {
424 ComObjPtr <HostFloppyDrive> hostFloppyDriveObj;
425 hostFloppyDriveObj.createObject();
426 hostFloppyDriveObj->init (Bstr (devName));
427 list.push_back (hostFloppyDriveObj);
428 }
429 }
430 }
431 }
432#else
433 /* PORTME */
434#endif
435
436 ComObjPtr<HostFloppyDriveCollection> collection;
437 collection.createObject();
438 collection->init (list);
439 collection.queryInterfaceTo(drives);
440 return S_OK;
441}
442
443#ifdef RT_OS_WINDOWS
444
445static bool IsTAPDevice(const char *guid)
446{
447 HKEY hNetcard;
448 LONG status;
449 DWORD len;
450 int i = 0;
451 bool ret = false;
452
453 status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, KEY_READ, &hNetcard);
454 if (status != ERROR_SUCCESS)
455 return false;
456
457 while(true)
458 {
459 char szEnumName[256];
460 char szNetCfgInstanceId[256];
461 DWORD dwKeyType;
462 HKEY hNetCardGUID;
463
464 len = sizeof(szEnumName);
465 status = RegEnumKeyExA(hNetcard, i, szEnumName, &len, NULL, NULL, NULL, NULL);
466 if (status != ERROR_SUCCESS)
467 break;
468
469 status = RegOpenKeyExA(hNetcard, szEnumName, 0, KEY_READ, &hNetCardGUID);
470 if (status == ERROR_SUCCESS)
471 {
472 len = sizeof (szNetCfgInstanceId);
473 status = RegQueryValueExA(hNetCardGUID, "NetCfgInstanceId", NULL, &dwKeyType, (LPBYTE)szNetCfgInstanceId, &len);
474 if (status == ERROR_SUCCESS && dwKeyType == REG_SZ)
475 {
476 char szNetProductName[256];
477 char szNetProviderName[256];
478
479 szNetProductName[0] = 0;
480 len = sizeof(szNetProductName);
481 status = RegQueryValueExA(hNetCardGUID, "ProductName", NULL, &dwKeyType, (LPBYTE)szNetProductName, &len);
482
483 szNetProviderName[0] = 0;
484 len = sizeof(szNetProviderName);
485 status = RegQueryValueExA(hNetCardGUID, "ProviderName", NULL, &dwKeyType, (LPBYTE)szNetProviderName, &len);
486
487 if ( !strcmp(szNetCfgInstanceId, guid)
488 && !strcmp(szNetProductName, "VirtualBox TAP Adapter")
489 && (!strcmp(szNetProviderName, "innotek GmbH") || !strcmp(szNetProviderName, "Sun Microsystems, Inc.")))
490 {
491 ret = true;
492 RegCloseKey(hNetCardGUID);
493 break;
494 }
495 }
496 RegCloseKey(hNetCardGUID);
497 }
498 ++i;
499 }
500
501 RegCloseKey (hNetcard);
502 return ret;
503}
504
505/**
506 * Returns a list of host network interfaces.
507 *
508 * @returns COM status code
509 * @param drives address of result pointer
510 */
511STDMETHODIMP Host::COMGETTER(NetworkInterfaces) (IHostNetworkInterfaceCollection **networkInterfaces)
512{
513 if (!networkInterfaces)
514 return E_POINTER;
515 AutoWriteLock alock (this);
516 CHECK_READY();
517
518 std::list <ComObjPtr <HostNetworkInterface> > list;
519
520 static const char *NetworkKey = "SYSTEM\\CurrentControlSet\\Control\\Network\\"
521 "{4D36E972-E325-11CE-BFC1-08002BE10318}";
522 HKEY hCtrlNet;
523 LONG status;
524 DWORD len;
525 status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, NetworkKey, 0, KEY_READ, &hCtrlNet);
526 if (status != ERROR_SUCCESS)
527 return setError (E_FAIL, tr("Could not open registry key \"%s\""), NetworkKey);
528
529 for (int i = 0;; ++ i)
530 {
531 char szNetworkGUID [256];
532 HKEY hConnection;
533 char szNetworkConnection [256];
534
535 len = sizeof (szNetworkGUID);
536 status = RegEnumKeyExA (hCtrlNet, i, szNetworkGUID, &len, NULL, NULL, NULL, NULL);
537 if (status != ERROR_SUCCESS)
538 break;
539
540 if (!IsTAPDevice(szNetworkGUID))
541 continue;
542
543 RTStrPrintf (szNetworkConnection, sizeof (szNetworkConnection),
544 "%s\\Connection", szNetworkGUID);
545 status = RegOpenKeyExA (hCtrlNet, szNetworkConnection, 0, KEY_READ, &hConnection);
546 if (status == ERROR_SUCCESS)
547 {
548 DWORD dwKeyType;
549 status = RegQueryValueExW (hConnection, TEXT("Name"), NULL,
550 &dwKeyType, NULL, &len);
551 if (status == ERROR_SUCCESS && dwKeyType == REG_SZ)
552 {
553 size_t uniLen = (len + sizeof (OLECHAR) - 1) / sizeof (OLECHAR);
554 Bstr name (uniLen + 1 /* extra zero */);
555 status = RegQueryValueExW (hConnection, TEXT("Name"), NULL,
556 &dwKeyType, (LPBYTE) name.mutableRaw(), &len);
557 if (status == ERROR_SUCCESS)
558 {
559 RTLogPrintf("Connection name %ls\n", name.mutableRaw());
560 /* put a trailing zero, just in case (see MSDN) */
561 name.mutableRaw() [uniLen] = 0;
562 /* create a new object and add it to the list */
563 ComObjPtr <HostNetworkInterface> iface;
564 iface.createObject();
565 /* remove the curly bracket at the end */
566 szNetworkGUID [strlen(szNetworkGUID) - 1] = '\0';
567 if (SUCCEEDED (iface->init (name, Guid (szNetworkGUID + 1))))
568 list.push_back (iface);
569 }
570 }
571 RegCloseKey (hConnection);
572 }
573 }
574 RegCloseKey (hCtrlNet);
575
576 ComObjPtr <HostNetworkInterfaceCollection> collection;
577 collection.createObject();
578 collection->init (list);
579 collection.queryInterfaceTo (networkInterfaces);
580 return S_OK;
581}
582#endif /* RT_OS_WINDOWS */
583
584STDMETHODIMP Host::COMGETTER(USBDevices)(IHostUSBDeviceCollection **aUSBDevices)
585{
586#ifdef VBOX_WITH_USB
587 if (!aUSBDevices)
588 return E_POINTER;
589
590 AutoWriteLock alock (this);
591 CHECK_READY();
592
593 MultiResult rc = checkUSBProxyService();
594 CheckComRCReturnRC (rc);
595
596 return mUSBProxyService->getDeviceCollection (aUSBDevices);
597
598#else
599 /* Note: The GUI depends on this method returning E_NOTIMPL with no
600 * extended error info to indicate that USB is simply not available
601 * (w/o treting it as a failure), for example, as in OSE */
602 return E_NOTIMPL;
603#endif
604}
605
606STDMETHODIMP Host::COMGETTER(USBDeviceFilters) (IHostUSBDeviceFilterCollection **aUSBDeviceFilters)
607{
608#ifdef VBOX_WITH_USB
609 if (!aUSBDeviceFilters)
610 return E_POINTER;
611
612 AutoWriteLock alock (this);
613 CHECK_READY();
614
615 MultiResult rc = checkUSBProxyService();
616 CheckComRCReturnRC (rc);
617
618 ComObjPtr <HostUSBDeviceFilterCollection> collection;
619 collection.createObject();
620 collection->init (mUSBDeviceFilters);
621 collection.queryInterfaceTo (aUSBDeviceFilters);
622
623 return rc;
624#else
625 /* Note: The GUI depends on this method returning E_NOTIMPL with no
626 * extended error info to indicate that USB is simply not available
627 * (w/o treting it as a failure), for example, as in OSE */
628 return E_NOTIMPL;
629#endif
630}
631
632/**
633 * Returns the number of installed logical processors
634 *
635 * @returns COM status code
636 * @param count address of result variable
637 */
638STDMETHODIMP Host::COMGETTER(ProcessorCount)(ULONG *count)
639{
640 if (!count)
641 return E_POINTER;
642 AutoWriteLock alock (this);
643 CHECK_READY();
644 *count = RTSystemProcessorGetCount();
645 return S_OK;
646}
647
648/**
649 * Returns the (approximate) speed of the host CPU in MHz
650 *
651 * @returns COM status code
652 * @param speed address of result variable
653 */
654STDMETHODIMP Host::COMGETTER(ProcessorSpeed)(ULONG *speed)
655{
656 if (!speed)
657 return E_POINTER;
658 AutoWriteLock alock (this);
659 CHECK_READY();
660 /** @todo Add a runtime function for this which uses GIP. */
661 return S_OK;
662}
663/**
664 * Returns a description string for the host CPU
665 *
666 * @returns COM status code
667 * @param description address of result variable
668 */
669STDMETHODIMP Host::COMGETTER(ProcessorDescription)(BSTR *description)
670{
671 if (!description)
672 return E_POINTER;
673 AutoWriteLock alock (this);
674 CHECK_READY();
675 /** @todo */
676 return S_OK;
677}
678
679
680/**
681 * Returns the amount of installed system memory in megabytes
682 *
683 * @returns COM status code
684 * @param size address of result variable
685 */
686STDMETHODIMP Host::COMGETTER(MemorySize)(ULONG *size)
687{
688 if (!size)
689 return E_POINTER;
690 AutoWriteLock alock (this);
691 CHECK_READY();
692 /** @todo */
693 return S_OK;
694}
695
696/**
697 * Returns the current system memory free space in megabytes
698 *
699 * @returns COM status code
700 * @param available address of result variable
701 */
702STDMETHODIMP Host::COMGETTER(MemoryAvailable)(ULONG *available)
703{
704 if (!available)
705 return E_POINTER;
706 AutoWriteLock alock (this);
707 CHECK_READY();
708 /** @todo */
709 return S_OK;
710}
711
712/**
713 * Returns the name string of the host operating system
714 *
715 * @returns COM status code
716 * @param os address of result variable
717 */
718STDMETHODIMP Host::COMGETTER(OperatingSystem)(BSTR *os)
719{
720 if (!os)
721 return E_POINTER;
722 AutoWriteLock alock (this);
723 CHECK_READY();
724 /** @todo */
725 return S_OK;
726}
727
728/**
729 * Returns the version string of the host operating system
730 *
731 * @returns COM status code
732 * @param os address of result variable
733 */
734STDMETHODIMP Host::COMGETTER(OSVersion)(BSTR *version)
735{
736 if (!version)
737 return E_POINTER;
738 AutoWriteLock alock (this);
739 CHECK_READY();
740 /** @todo */
741 return S_OK;
742}
743
744/**
745 * Returns the current host time in milliseconds since 1970-01-01 UTC.
746 *
747 * @returns COM status code
748 * @param time address of result variable
749 */
750STDMETHODIMP Host::COMGETTER(UTCTime)(LONG64 *aUTCTime)
751{
752 if (!aUTCTime)
753 return E_POINTER;
754 AutoWriteLock alock (this);
755 CHECK_READY();
756 RTTIMESPEC now;
757 *aUTCTime = RTTimeSpecGetMilli(RTTimeNow(&now));
758 return S_OK;
759}
760
761// IHost methods
762////////////////////////////////////////////////////////////////////////////////
763
764#ifdef RT_OS_WINDOWS
765
766/**
767 * Returns TRUE if the Windows version is 6.0 or greater (i.e. it's Vista and
768 * later OSes) and it has the UAC (User Account Control) feature enabled.
769 */
770static BOOL IsUACEnabled()
771{
772 LONG rc = 0;
773
774 OSVERSIONINFOEX info;
775 ZeroMemory (&info, sizeof (OSVERSIONINFOEX));
776 info.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);
777 rc = GetVersionEx ((OSVERSIONINFO *) &info);
778 AssertReturn (rc != 0, FALSE);
779
780 LogFlowFunc (("dwMajorVersion=%d, dwMinorVersion=%d\n",
781 info.dwMajorVersion, info.dwMinorVersion));
782
783 /* we are interested only in Vista (and newer versions...). In all
784 * earlier versions UAC is not present. */
785 if (info.dwMajorVersion < 6)
786 return FALSE;
787
788 /* the default EnableLUA value is 1 (Enabled) */
789 DWORD dwEnableLUA = 1;
790
791 HKEY hKey;
792 rc = RegOpenKeyExA (HKEY_LOCAL_MACHINE,
793 "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
794 0, KEY_QUERY_VALUE, &hKey);
795
796 Assert (rc == ERROR_SUCCESS || rc == ERROR_PATH_NOT_FOUND);
797 if (rc == ERROR_SUCCESS)
798 {
799
800 DWORD cbEnableLUA = sizeof (dwEnableLUA);
801 rc = RegQueryValueExA (hKey, "EnableLUA", NULL, NULL,
802 (LPBYTE) &dwEnableLUA, &cbEnableLUA);
803
804 RegCloseKey (hKey);
805
806 Assert (rc == ERROR_SUCCESS || rc == ERROR_FILE_NOT_FOUND);
807 }
808
809 LogFlowFunc (("rc=%d, dwEnableLUA=%d\n", rc, dwEnableLUA));
810
811 return dwEnableLUA == 1;
812}
813
814struct NetworkInterfaceHelperClientData
815{
816 SVCHlpMsg::Code msgCode;
817 /* for SVCHlpMsg::CreateHostNetworkInterface */
818 Bstr name;
819 ComObjPtr <HostNetworkInterface> iface;
820 /* for SVCHlpMsg::RemoveHostNetworkInterface */
821 Guid guid;
822};
823
824STDMETHODIMP
825Host::CreateHostNetworkInterface (INPTR BSTR aName,
826 IHostNetworkInterface **aHostNetworkInterface,
827 IProgress **aProgress)
828{
829 if (!aName)
830 return E_INVALIDARG;
831 if (!aHostNetworkInterface)
832 return E_POINTER;
833 if (!aProgress)
834 return E_POINTER;
835
836 AutoWriteLock alock (this);
837 CHECK_READY();
838
839 HRESULT rc = S_OK;
840
841 /* first check whether an interface with the given name already exists */
842 {
843 ComPtr <IHostNetworkInterfaceCollection> coll;
844 rc = COMGETTER(NetworkInterfaces) (coll.asOutParam());
845 CheckComRCReturnRC (rc);
846 ComPtr <IHostNetworkInterface> iface;
847 if (SUCCEEDED (coll->FindByName (aName, iface.asOutParam())))
848 return setError (E_FAIL,
849 tr ("Host network interface '%ls' already exists"), aName);
850 }
851
852 /* create a progress object */
853 ComObjPtr <Progress> progress;
854 progress.createObject();
855 rc = progress->init (mParent, static_cast <IHost *> (this),
856 Bstr (tr ("Creating host network interface")),
857 FALSE /* aCancelable */);
858 CheckComRCReturnRC (rc);
859 progress.queryInterfaceTo (aProgress);
860
861 /* create a new uninitialized host interface object */
862 ComObjPtr <HostNetworkInterface> iface;
863 iface.createObject();
864 iface.queryInterfaceTo (aHostNetworkInterface);
865
866 /* create the networkInterfaceHelperClient() argument */
867 std::auto_ptr <NetworkInterfaceHelperClientData>
868 d (new NetworkInterfaceHelperClientData());
869 AssertReturn (d.get(), E_OUTOFMEMORY);
870
871 d->msgCode = SVCHlpMsg::CreateHostNetworkInterface;
872 d->name = aName;
873 d->iface = iface;
874
875 rc = mParent->startSVCHelperClient (
876 IsUACEnabled() == TRUE /* aPrivileged */,
877 networkInterfaceHelperClient,
878 static_cast <void *> (d.get()),
879 progress);
880
881 if (SUCCEEDED (rc))
882 {
883 /* d is now owned by networkInterfaceHelperClient(), so release it */
884 d.release();
885 }
886
887 return rc;
888}
889
890STDMETHODIMP
891Host::RemoveHostNetworkInterface (INPTR GUIDPARAM aId,
892 IHostNetworkInterface **aHostNetworkInterface,
893 IProgress **aProgress)
894{
895 if (!aHostNetworkInterface)
896 return E_POINTER;
897 if (!aProgress)
898 return E_POINTER;
899
900 AutoWriteLock alock (this);
901 CHECK_READY();
902
903 HRESULT rc = S_OK;
904
905 /* first check whether an interface with the given name already exists */
906 {
907 ComPtr <IHostNetworkInterfaceCollection> coll;
908 rc = COMGETTER(NetworkInterfaces) (coll.asOutParam());
909 CheckComRCReturnRC (rc);
910 ComPtr <IHostNetworkInterface> iface;
911 if (FAILED (coll->FindById (aId, iface.asOutParam())))
912 return setError (E_FAIL,
913 tr ("Host network interface with UUID {%Vuuid} does not exist"),
914 Guid (aId).raw());
915
916 /* return the object to be removed to the caller */
917 iface.queryInterfaceTo (aHostNetworkInterface);
918 }
919
920 /* create a progress object */
921 ComObjPtr <Progress> progress;
922 progress.createObject();
923 rc = progress->init (mParent, static_cast <IHost *> (this),
924 Bstr (tr ("Removing host network interface")),
925 FALSE /* aCancelable */);
926 CheckComRCReturnRC (rc);
927 progress.queryInterfaceTo (aProgress);
928
929 /* create the networkInterfaceHelperClient() argument */
930 std::auto_ptr <NetworkInterfaceHelperClientData>
931 d (new NetworkInterfaceHelperClientData());
932 AssertReturn (d.get(), E_OUTOFMEMORY);
933
934 d->msgCode = SVCHlpMsg::RemoveHostNetworkInterface;
935 d->guid = aId;
936
937 rc = mParent->startSVCHelperClient (
938 IsUACEnabled() == TRUE /* aPrivileged */,
939 networkInterfaceHelperClient,
940 static_cast <void *> (d.get()),
941 progress);
942
943 if (SUCCEEDED (rc))
944 {
945 /* d is now owned by networkInterfaceHelperClient(), so release it */
946 d.release();
947 }
948
949 return rc;
950}
951
952#endif /* RT_OS_WINDOWS */
953
954STDMETHODIMP Host::CreateUSBDeviceFilter (INPTR BSTR aName, IHostUSBDeviceFilter **aFilter)
955{
956#ifdef VBOX_WITH_USB
957 if (!aFilter)
958 return E_POINTER;
959
960 if (!aName || *aName == 0)
961 return E_INVALIDARG;
962
963 AutoWriteLock alock (this);
964 CHECK_READY();
965
966 ComObjPtr <HostUSBDeviceFilter> filter;
967 filter.createObject();
968 HRESULT rc = filter->init (this, aName);
969 ComAssertComRCRet (rc, rc);
970 rc = filter.queryInterfaceTo (aFilter);
971 AssertComRCReturn (rc, rc);
972 return S_OK;
973#else
974 /* Note: The GUI depends on this method returning E_NOTIMPL with no
975 * extended error info to indicate that USB is simply not available
976 * (w/o treting it as a failure), for example, as in OSE */
977 return E_NOTIMPL;
978#endif
979}
980
981STDMETHODIMP Host::InsertUSBDeviceFilter (ULONG aPosition, IHostUSBDeviceFilter *aFilter)
982{
983#ifdef VBOX_WITH_USB
984 if (!aFilter)
985 return E_INVALIDARG;
986
987 /* Note: HostUSBDeviceFilter and USBProxyService also uses this lock. */
988 AutoWriteLock alock (this);
989 CHECK_READY();
990
991 MultiResult rc = checkUSBProxyService();
992 CheckComRCReturnRC (rc);
993
994 ComObjPtr <HostUSBDeviceFilter> filter = getDependentChild (aFilter);
995 if (!filter)
996 return setError (E_INVALIDARG,
997 tr ("The given USB device filter is not created within "
998 "this VirtualBox instance"));
999
1000 if (filter->mInList)
1001 return setError (E_INVALIDARG,
1002 tr ("The given USB device filter is already in the list"));
1003
1004 /* iterate to the position... */
1005 USBDeviceFilterList::iterator it = mUSBDeviceFilters.begin();
1006 std::advance (it, aPosition);
1007 /* ...and insert */
1008 mUSBDeviceFilters.insert (it, filter);
1009 filter->mInList = true;
1010
1011 /* notify the proxy (only when the filter is active) */
1012 if (mUSBProxyService->isActive() && filter->data().mActive)
1013 {
1014 ComAssertRet (filter->id() == NULL, E_FAIL);
1015 filter->id() = mUSBProxyService->insertFilter (&filter->data().mUSBFilter);
1016 }
1017
1018 /* save the global settings */
1019 alock.unlock();
1020 return rc = mParent->saveSettings();
1021#else
1022 /* Note: The GUI depends on this method returning E_NOTIMPL with no
1023 * extended error info to indicate that USB is simply not available
1024 * (w/o treting it as a failure), for example, as in OSE */
1025 return E_NOTIMPL;
1026#endif
1027}
1028
1029STDMETHODIMP Host::RemoveUSBDeviceFilter (ULONG aPosition, IHostUSBDeviceFilter **aFilter)
1030{
1031#ifdef VBOX_WITH_USB
1032 if (!aFilter)
1033 return E_POINTER;
1034
1035 /* Note: HostUSBDeviceFilter and USBProxyService also uses this lock. */
1036 AutoWriteLock alock (this);
1037 CHECK_READY();
1038
1039 MultiResult rc = checkUSBProxyService();
1040 CheckComRCReturnRC (rc);
1041
1042 if (!mUSBDeviceFilters.size())
1043 return setError (E_INVALIDARG,
1044 tr ("The USB device filter list is empty"));
1045
1046 if (aPosition >= mUSBDeviceFilters.size())
1047 return setError (E_INVALIDARG,
1048 tr ("Invalid position: %lu (must be in range [0, %lu])"),
1049 aPosition, mUSBDeviceFilters.size() - 1);
1050
1051 ComObjPtr <HostUSBDeviceFilter> filter;
1052 {
1053 /* iterate to the position... */
1054 USBDeviceFilterList::iterator it = mUSBDeviceFilters.begin();
1055 std::advance (it, aPosition);
1056 /* ...get an element from there... */
1057 filter = *it;
1058 /* ...and remove */
1059 filter->mInList = false;
1060 mUSBDeviceFilters.erase (it);
1061 }
1062
1063 filter.queryInterfaceTo (aFilter);
1064
1065 /* notify the proxy (only when the filter is active) */
1066 if (mUSBProxyService->isActive() && filter->data().mActive)
1067 {
1068 ComAssertRet (filter->id() != NULL, E_FAIL);
1069 mUSBProxyService->removeFilter (filter->id());
1070 filter->id() = NULL;
1071 }
1072
1073 /* save the global settings */
1074 alock.unlock();
1075 return rc = mParent->saveSettings();
1076#else
1077 /* Note: The GUI depends on this method returning E_NOTIMPL with no
1078 * extended error info to indicate that USB is simply not available
1079 * (w/o treting it as a failure), for example, as in OSE */
1080 return E_NOTIMPL;
1081#endif
1082}
1083
1084// public methods only for internal purposes
1085////////////////////////////////////////////////////////////////////////////////
1086
1087HRESULT Host::loadSettings (const settings::Key &aGlobal)
1088{
1089 using namespace settings;
1090
1091 AutoWriteLock alock (this);
1092 CHECK_READY();
1093
1094 AssertReturn (!aGlobal.isNull(), E_FAIL);
1095
1096 HRESULT rc = S_OK;
1097
1098#ifdef VBOX_WITH_USB
1099 Key::List filters = aGlobal.key ("USBDeviceFilters").keys ("DeviceFilter");
1100 for (Key::List::const_iterator it = filters.begin();
1101 it != filters.end(); ++ it)
1102 {
1103 Bstr name = (*it).stringValue ("name");
1104 bool active = (*it).value <bool> ("active");
1105
1106 Bstr vendorId = (*it).stringValue ("vendorId");
1107 Bstr productId = (*it).stringValue ("productId");
1108 Bstr revision = (*it).stringValue ("revision");
1109 Bstr manufacturer = (*it).stringValue ("manufacturer");
1110 Bstr product = (*it).stringValue ("product");
1111 Bstr serialNumber = (*it).stringValue ("serialNumber");
1112 Bstr port = (*it).stringValue ("port");
1113
1114 USBDeviceFilterAction_T action;
1115 action = USBDeviceFilterAction_Ignore;
1116 const char *actionStr = (*it).stringValue ("action");
1117 if (strcmp (actionStr, "Ignore") == 0)
1118 action = USBDeviceFilterAction_Ignore;
1119 else
1120 if (strcmp (actionStr, "Hold") == 0)
1121 action = USBDeviceFilterAction_Hold;
1122 else
1123 AssertMsgFailed (("Invalid action: '%s'\n", actionStr));
1124
1125 ComObjPtr <HostUSBDeviceFilter> filterObj;
1126 filterObj.createObject();
1127 rc = filterObj->init (this,
1128 name, active, vendorId, productId, revision,
1129 manufacturer, product, serialNumber, port,
1130 action);
1131 /* error info is set by init() when appropriate */
1132 CheckComRCBreakRC (rc);
1133
1134 mUSBDeviceFilters.push_back (filterObj);
1135 filterObj->mInList = true;
1136
1137 /* notify the proxy (only when the filter is active) */
1138 if (filterObj->data().mActive)
1139 {
1140 HostUSBDeviceFilter *flt = filterObj; /* resolve ambiguity */
1141 flt->id() = mUSBProxyService->insertFilter (&filterObj->data().mUSBFilter);
1142 }
1143 }
1144#endif /* VBOX_WITH_USB */
1145
1146 return rc;
1147}
1148
1149HRESULT Host::saveSettings (settings::Key &aGlobal)
1150{
1151 using namespace settings;
1152
1153 AutoWriteLock alock (this);
1154 CHECK_READY();
1155
1156 ComAssertRet (!aGlobal.isNull(), E_FAIL);
1157
1158#ifdef VBOX_WITH_USB
1159 /* first, delete the entry */
1160 Key filters = aGlobal.findKey ("USBDeviceFilters");
1161 if (!filters.isNull())
1162 filters.zap();
1163 /* then, recreate it */
1164 filters = aGlobal.createKey ("USBDeviceFilters");
1165
1166 USBDeviceFilterList::const_iterator it = mUSBDeviceFilters.begin();
1167 while (it != mUSBDeviceFilters.end())
1168 {
1169 AutoWriteLock filterLock (*it);
1170 const HostUSBDeviceFilter::Data &data = (*it)->data();
1171
1172 Key filter = filters.appendKey ("DeviceFilter");
1173
1174 filter.setValue <Bstr> ("name", data.mName);
1175 filter.setValue <bool> ("active", !!data.mActive);
1176
1177 /* all are optional */
1178 Bstr str;
1179 (*it)->COMGETTER (VendorId) (str.asOutParam());
1180 if (!str.isNull())
1181 filter.setValue <Bstr> ("vendorId", str);
1182
1183 (*it)->COMGETTER (ProductId) (str.asOutParam());
1184 if (!str.isNull())
1185 filter.setValue <Bstr> ("productId", str);
1186
1187 (*it)->COMGETTER (Revision) (str.asOutParam());
1188 if (!str.isNull())
1189 filter.setValue <Bstr> ("revision", str);
1190
1191 (*it)->COMGETTER (Manufacturer) (str.asOutParam());
1192 if (!str.isNull())
1193 filter.setValue <Bstr> ("manufacturer", str);
1194
1195 (*it)->COMGETTER (Product) (str.asOutParam());
1196 if (!str.isNull())
1197 filter.setValue <Bstr> ("product", str);
1198
1199 (*it)->COMGETTER (SerialNumber) (str.asOutParam());
1200 if (!str.isNull())
1201 filter.setValue <Bstr> ("serialNumber", str);
1202
1203 (*it)->COMGETTER (Port) (str.asOutParam());
1204 if (!str.isNull())
1205 filter.setValue <Bstr> ("port", str);
1206
1207 /* action is mandatory */
1208 USBDeviceFilterAction_T action = USBDeviceFilterAction_Null;
1209 (*it)->COMGETTER (Action) (&action);
1210 if (action == USBDeviceFilterAction_Ignore)
1211 filter.setStringValue ("action", "Ignore");
1212 else if (action == USBDeviceFilterAction_Hold)
1213 filter.setStringValue ("action", "Hold");
1214 else
1215 AssertMsgFailed (("Invalid action: %d\n", action));
1216
1217 ++ it;
1218 }
1219#endif /* VBOX_WITH_USB */
1220
1221 return S_OK;
1222}
1223
1224#ifdef VBOX_WITH_USB
1225/**
1226 * Called by setter methods of all USB device filters.
1227 */
1228HRESULT Host::onUSBDeviceFilterChange (HostUSBDeviceFilter *aFilter,
1229 BOOL aActiveChanged /* = FALSE */)
1230{
1231 AutoWriteLock alock (this);
1232 CHECK_READY();
1233
1234 if (aFilter->mInList)
1235 {
1236 if (aActiveChanged)
1237 {
1238 // insert/remove the filter from the proxy
1239 if (aFilter->data().mActive)
1240 {
1241 ComAssertRet (aFilter->id() == NULL, E_FAIL);
1242 aFilter->id() = mUSBProxyService->insertFilter (&aFilter->data().mUSBFilter);
1243 }
1244 else
1245 {
1246 ComAssertRet (aFilter->id() != NULL, E_FAIL);
1247 mUSBProxyService->removeFilter (aFilter->id());
1248 aFilter->id() = NULL;
1249 }
1250 }
1251 else
1252 {
1253 if (aFilter->data().mActive)
1254 {
1255 // update the filter in the proxy
1256 ComAssertRet (aFilter->id() != NULL, E_FAIL);
1257 mUSBProxyService->removeFilter (aFilter->id());
1258 aFilter->id() = mUSBProxyService->insertFilter (&aFilter->data().mUSBFilter);
1259 }
1260 }
1261
1262 // save the global settings... yeah, on every single filter property change
1263 alock.unlock();
1264 return mParent->saveSettings();
1265 }
1266
1267 return S_OK;
1268}
1269
1270
1271/**
1272 * Interface for obtaining a copy of the USBDeviceFilterList,
1273 * used by the USBProxyService.
1274 *
1275 * @param aGlobalFilters Where to put the global filter list copy.
1276 * @param aMachines Where to put the machine vector.
1277 */
1278void Host::getUSBFilters(Host::USBDeviceFilterList *aGlobalFilters, VirtualBox::SessionMachineVector *aMachines)
1279{
1280 AutoWriteLock alock (this);
1281
1282 mParent->getOpenedMachines (*aMachines);
1283 *aGlobalFilters = mUSBDeviceFilters;
1284}
1285
1286#endif /* VBOX_WITH_USB */
1287
1288// private methods
1289////////////////////////////////////////////////////////////////////////////////
1290
1291#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1292# ifdef VBOX_USE_LIBHAL
1293/**
1294 * Helper function to query the hal subsystem for information about DVD drives attached to the
1295 * system.
1296 *
1297 * @returns true if information was successfully obtained, false otherwise
1298 * @retval list drives found will be attached to this list
1299 */
1300bool Host::getDVDInfoFromHal(std::list <ComObjPtr <HostDVDDrive> > &list)
1301{
1302 bool halSuccess = false;
1303 DBusError dbusError;
1304 if (!gLibHalCheckPresence())
1305 return false;
1306 gDBusErrorInit (&dbusError);
1307 DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError);
1308 if (dbusConnection != 0)
1309 {
1310 LibHalContext *halContext = gLibHalCtxNew();
1311 if (halContext != 0)
1312 {
1313 if (gLibHalCtxSetDBusConnection (halContext, dbusConnection))
1314 {
1315 if (gLibHalCtxInit(halContext, &dbusError))
1316 {
1317 int numDevices;
1318 char **halDevices = gLibHalFindDeviceStringMatch(halContext,
1319 "storage.drive_type", "cdrom",
1320 &numDevices, &dbusError);
1321 if (halDevices != 0)
1322 {
1323 /* Hal is installed and working, so if no devices are reported, assume
1324 that there are none. */
1325 halSuccess = true;
1326 for (int i = 0; i < numDevices; i++)
1327 {
1328 char *devNode = gLibHalDeviceGetPropertyString(halContext,
1329 halDevices[i], "block.device", &dbusError);
1330#ifdef RT_OS_SOLARIS
1331 /* The CD/DVD ioctls work only for raw device nodes. */
1332 char *tmp = getfullrawname(devNode);
1333 gLibHalFreeString(devNode);
1334 devNode = tmp;
1335#endif
1336 if (devNode != 0)
1337 {
1338// if (validateDevice(devNode, true))
1339// {
1340 Utf8Str description;
1341 char *vendor, *product;
1342 /* We do not check the error here, as this field may
1343 not even exist. */
1344 vendor = gLibHalDeviceGetPropertyString(halContext,
1345 halDevices[i], "info.vendor", 0);
1346 product = gLibHalDeviceGetPropertyString(halContext,
1347 halDevices[i], "info.product", &dbusError);
1348 if ((product != 0 && product[0] != 0))
1349 {
1350 if ((vendor != 0) && (vendor[0] != 0))
1351 {
1352 description = Utf8StrFmt ("%s %s",
1353 vendor, product);
1354 }
1355 else
1356 {
1357 description = product;
1358 }
1359 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1360 hostDVDDriveObj.createObject();
1361 hostDVDDriveObj->init (Bstr (devNode),
1362 Bstr (halDevices[i]),
1363 Bstr (description));
1364 list.push_back (hostDVDDriveObj);
1365 }
1366 else
1367 {
1368 if (product == 0)
1369 {
1370 LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
1371 halDevices[i], dbusError.name, dbusError.message));
1372 gDBusErrorFree(&dbusError);
1373 }
1374 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1375 hostDVDDriveObj.createObject();
1376 hostDVDDriveObj->init (Bstr (devNode),
1377 Bstr (halDevices[i]));
1378 list.push_back (hostDVDDriveObj);
1379 }
1380 if (vendor != 0)
1381 {
1382 gLibHalFreeString(vendor);
1383 }
1384 if (product != 0)
1385 {
1386 gLibHalFreeString(product);
1387 }
1388// }
1389// else
1390// {
1391// LogRel(("Host::COMGETTER(DVDDrives): failed to validate the block device %s as a DVD drive\n"));
1392// }
1393#ifndef RT_OS_SOLARIS
1394 gLibHalFreeString(devNode);
1395#else
1396 free(devNode);
1397#endif
1398 }
1399 else
1400 {
1401 LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
1402 halDevices[i], dbusError.name, dbusError.message));
1403 gDBusErrorFree(&dbusError);
1404 }
1405 }
1406 gLibHalFreeStringArray(halDevices);
1407 }
1408 else
1409 {
1410 LogRel(("Host::COMGETTER(DVDDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1411 gDBusErrorFree(&dbusError);
1412 }
1413 if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */
1414 {
1415 LogRel(("Host::COMGETTER(DVDDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1416 gDBusErrorFree(&dbusError);
1417 }
1418 }
1419 else
1420 {
1421 LogRel(("Host::COMGETTER(DVDDrives): failed to initialise libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1422 gDBusErrorFree(&dbusError);
1423 }
1424 gLibHalCtxFree(halContext);
1425 }
1426 else
1427 {
1428 LogRel(("Host::COMGETTER(DVDDrives): failed to set libhal connection to dbus.\n"));
1429 }
1430 }
1431 else
1432 {
1433 LogRel(("Host::COMGETTER(DVDDrives): failed to get a libhal context - out of memory?\n"));
1434 }
1435 gDBusConnectionUnref(dbusConnection);
1436 }
1437 else
1438 {
1439 LogRel(("Host::COMGETTER(DVDDrives): failed to connect to dbus. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1440 gDBusErrorFree(&dbusError);
1441 }
1442 return halSuccess;
1443}
1444
1445
1446/**
1447 * Helper function to query the hal subsystem for information about floppy drives attached to the
1448 * system.
1449 *
1450 * @returns true if information was successfully obtained, false otherwise
1451 * @retval list drives found will be attached to this list
1452 */
1453bool Host::getFloppyInfoFromHal(std::list <ComObjPtr <HostFloppyDrive> > &list)
1454{
1455 bool halSuccess = false;
1456 DBusError dbusError;
1457 if (!gLibHalCheckPresence())
1458 return false;
1459 gDBusErrorInit (&dbusError);
1460 DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError);
1461 if (dbusConnection != 0)
1462 {
1463 LibHalContext *halContext = gLibHalCtxNew();
1464 if (halContext != 0)
1465 {
1466 if (gLibHalCtxSetDBusConnection (halContext, dbusConnection))
1467 {
1468 if (gLibHalCtxInit(halContext, &dbusError))
1469 {
1470 int numDevices;
1471 char **halDevices = gLibHalFindDeviceStringMatch(halContext,
1472 "storage.drive_type", "floppy",
1473 &numDevices, &dbusError);
1474 if (halDevices != 0)
1475 {
1476 /* Hal is installed and working, so if no devices are reported, assume
1477 that there are none. */
1478 halSuccess = true;
1479 for (int i = 0; i < numDevices; i++)
1480 {
1481 char *driveType = gLibHalDeviceGetPropertyString(halContext,
1482 halDevices[i], "storage.drive_type", 0);
1483 if (driveType != 0)
1484 {
1485 if (strcmp(driveType, "floppy") != 0)
1486 {
1487 gLibHalFreeString(driveType);
1488 continue;
1489 }
1490 gLibHalFreeString(driveType);
1491 }
1492 else
1493 {
1494 /* An error occurred. The attribute "storage.drive_type"
1495 probably didn't exist. */
1496 continue;
1497 }
1498 char *devNode = gLibHalDeviceGetPropertyString(halContext,
1499 halDevices[i], "block.device", &dbusError);
1500 if (devNode != 0)
1501 {
1502// if (validateDevice(devNode, false))
1503// {
1504 Utf8Str description;
1505 char *vendor, *product;
1506 /* We do not check the error here, as this field may
1507 not even exist. */
1508 vendor = gLibHalDeviceGetPropertyString(halContext,
1509 halDevices[i], "info.vendor", 0);
1510 product = gLibHalDeviceGetPropertyString(halContext,
1511 halDevices[i], "info.product", &dbusError);
1512 if ((product != 0) && (product[0] != 0))
1513 {
1514 if ((vendor != 0) && (vendor[0] != 0))
1515 {
1516 description = Utf8StrFmt ("%s %s",
1517 vendor, product);
1518 }
1519 else
1520 {
1521 description = product;
1522 }
1523 ComObjPtr <HostFloppyDrive> hostFloppyDrive;
1524 hostFloppyDrive.createObject();
1525 hostFloppyDrive->init (Bstr (devNode),
1526 Bstr (halDevices[i]),
1527 Bstr (description));
1528 list.push_back (hostFloppyDrive);
1529 }
1530 else
1531 {
1532 if (product == 0)
1533 {
1534 LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
1535 halDevices[i], dbusError.name, dbusError.message));
1536 gDBusErrorFree(&dbusError);
1537 }
1538 ComObjPtr <HostFloppyDrive> hostFloppyDrive;
1539 hostFloppyDrive.createObject();
1540 hostFloppyDrive->init (Bstr (devNode),
1541 Bstr (halDevices[i]));
1542 list.push_back (hostFloppyDrive);
1543 }
1544 if (vendor != 0)
1545 {
1546 gLibHalFreeString(vendor);
1547 }
1548 if (product != 0)
1549 {
1550 gLibHalFreeString(product);
1551 }
1552// }
1553// else
1554// {
1555// LogRel(("Host::COMGETTER(FloppyDrives): failed to validate the block device %s as a floppy drive\n"));
1556// }
1557 gLibHalFreeString(devNode);
1558 }
1559 else
1560 {
1561 LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
1562 halDevices[i], dbusError.name, dbusError.message));
1563 gDBusErrorFree(&dbusError);
1564 }
1565 }
1566 gLibHalFreeStringArray(halDevices);
1567 }
1568 else
1569 {
1570 LogRel(("Host::COMGETTER(FloppyDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1571 gDBusErrorFree(&dbusError);
1572 }
1573 if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */
1574 {
1575 LogRel(("Host::COMGETTER(FloppyDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1576 gDBusErrorFree(&dbusError);
1577 }
1578 }
1579 else
1580 {
1581 LogRel(("Host::COMGETTER(FloppyDrives): failed to initialise libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1582 gDBusErrorFree(&dbusError);
1583 }
1584 gLibHalCtxFree(halContext);
1585 }
1586 else
1587 {
1588 LogRel(("Host::COMGETTER(FloppyDrives): failed to set libhal connection to dbus.\n"));
1589 }
1590 }
1591 else
1592 {
1593 LogRel(("Host::COMGETTER(FloppyDrives): failed to get a libhal context - out of memory?\n"));
1594 }
1595 gDBusConnectionUnref(dbusConnection);
1596 }
1597 else
1598 {
1599 LogRel(("Host::COMGETTER(FloppyDrives): failed to connect to dbus. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1600 gDBusErrorFree(&dbusError);
1601 }
1602 return halSuccess;
1603}
1604# endif /* VBOX_USE_HAL defined */
1605
1606/**
1607 * Helper function to parse the given mount file and add found entries
1608 */
1609void Host::parseMountTable(char *mountTable, std::list <ComObjPtr <HostDVDDrive> > &list)
1610{
1611#ifdef RT_OS_LINUX
1612 FILE *mtab = setmntent(mountTable, "r");
1613 if (mtab)
1614 {
1615 struct mntent *mntent;
1616 char *mnt_type;
1617 char *mnt_dev;
1618 char *tmp;
1619 while ((mntent = getmntent(mtab)))
1620 {
1621 mnt_type = (char*)malloc(strlen(mntent->mnt_type) + 1);
1622 mnt_dev = (char*)malloc(strlen(mntent->mnt_fsname) + 1);
1623 strcpy(mnt_type, mntent->mnt_type);
1624 strcpy(mnt_dev, mntent->mnt_fsname);
1625 // supermount fs case
1626 if (strcmp(mnt_type, "supermount") == 0)
1627 {
1628 tmp = strstr(mntent->mnt_opts, "fs=");
1629 if (tmp)
1630 {
1631 free(mnt_type);
1632 mnt_type = strdup(tmp + strlen("fs="));
1633 if (mnt_type)
1634 {
1635 tmp = strchr(mnt_type, ',');
1636 if (tmp)
1637 *tmp = '\0';
1638 }
1639 }
1640 tmp = strstr(mntent->mnt_opts, "dev=");
1641 if (tmp)
1642 {
1643 free(mnt_dev);
1644 mnt_dev = strdup(tmp + strlen("dev="));
1645 if (mnt_dev)
1646 {
1647 tmp = strchr(mnt_dev, ',');
1648 if (tmp)
1649 *tmp = '\0';
1650 }
1651 }
1652 }
1653 // use strstr here to cover things fs types like "udf,iso9660"
1654 if (strstr(mnt_type, "iso9660") == 0)
1655 {
1656 /** @todo check whether we've already got the drive in our list! */
1657 if (validateDevice(mnt_dev, true))
1658 {
1659 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1660 hostDVDDriveObj.createObject();
1661 hostDVDDriveObj->init (Bstr (mnt_dev));
1662 list.push_back (hostDVDDriveObj);
1663 }
1664 }
1665 free(mnt_dev);
1666 free(mnt_type);
1667 }
1668 endmntent(mtab);
1669 }
1670#else // RT_OS_SOLARIS
1671 FILE *mntFile = fopen(mountTable, "r");
1672 if (mntFile)
1673 {
1674 struct mnttab mntTab;
1675 while (getmntent(mntFile, &mntTab) == 0)
1676 {
1677 char *mountName = strdup(mntTab.mnt_special);
1678 char *mountPoint = strdup(mntTab.mnt_mountp);
1679 char *mountFSType = strdup(mntTab.mnt_fstype);
1680
1681 // skip devices we are not interested in
1682 if ((*mountName && mountName[0] == '/') && // skip 'fake' devices (like -hosts, proc, fd, swap)
1683 (*mountFSType && (strcmp(mountFSType, "devfs") != 0 && // skip devfs (i.e. /devices)
1684 strcmp(mountFSType, "dev") != 0 && // skip dev (i.e. /dev)
1685 strcmp(mountFSType, "lofs") != 0)) && // skip loop-back file-system (lofs)
1686 (*mountPoint && strcmp(mountPoint, "/") != 0)) // skip point '/' (Can CD/DVD be mounted at '/' ???)
1687 {
1688 char *rawDevName = getfullrawname(mountName);
1689 if (validateDevice(rawDevName, true))
1690 {
1691 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1692 hostDVDDriveObj.createObject();
1693 hostDVDDriveObj->init (Bstr (rawDevName));
1694 list.push_back (hostDVDDriveObj);
1695 }
1696 free(rawDevName);
1697 }
1698
1699 free(mountName);
1700 free(mountPoint);
1701 free(mountFSType);
1702 }
1703
1704 fclose(mntFile);
1705 }
1706#endif
1707}
1708
1709/**
1710 * Helper function to check whether the given device node is a valid drive
1711 */
1712bool Host::validateDevice(const char *deviceNode, bool isCDROM)
1713{
1714 struct stat statInfo;
1715 bool retValue = false;
1716
1717 // sanity check
1718 if (!deviceNode)
1719 {
1720 return false;
1721 }
1722
1723 // first a simple stat() call
1724 if (stat(deviceNode, &statInfo) < 0)
1725 {
1726 return false;
1727 } else
1728 {
1729 if (isCDROM)
1730 {
1731 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
1732 {
1733 int fileHandle;
1734 // now try to open the device
1735 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
1736 if (fileHandle >= 0)
1737 {
1738 cdrom_subchnl cdChannelInfo;
1739 cdChannelInfo.cdsc_format = CDROM_MSF;
1740 // this call will finally reveal the whole truth
1741#ifdef RT_OS_LINUX
1742 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
1743 (errno == EIO) || (errno == ENOENT) ||
1744 (errno == EINVAL) || (errno == ENOMEDIUM))
1745#else
1746 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
1747 (errno == EIO) || (errno == ENOENT) ||
1748 (errno == EINVAL))
1749#endif
1750 {
1751 retValue = true;
1752 }
1753 close(fileHandle);
1754 }
1755 }
1756 } else
1757 {
1758 // floppy case
1759 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
1760 {
1761 /// @todo do some more testing, maybe a nice IOCTL!
1762 retValue = true;
1763 }
1764 }
1765 }
1766 return retValue;
1767}
1768#endif // RT_OS_LINUX || RT_OS_SOLARIS
1769
1770#ifdef VBOX_WITH_USB
1771/**
1772 * Checks for the presense and status of the USB Proxy Service.
1773 * Returns S_OK when the Proxy is present and OK, or E_FAIL and a
1774 * corresponding error message otherwise. Intended to be used by methods
1775 * that rely on the Proxy Service availability.
1776 *
1777 * @note This method may return a warning result code. It is recommended to use
1778 * MultiError to store the return value.
1779 *
1780 * @note Locks this object for reading.
1781 */
1782HRESULT Host::checkUSBProxyService()
1783{
1784 AutoWriteLock alock (this);
1785 CHECK_READY();
1786
1787 AssertReturn (mUSBProxyService, E_FAIL);
1788 if (!mUSBProxyService->isActive())
1789 {
1790 /* disable the USB controller completely to avoid assertions if the
1791 * USB proxy service could not start. */
1792
1793 if (mUSBProxyService->getLastError() == VERR_FILE_NOT_FOUND)
1794 return setWarning (E_FAIL,
1795 tr ("Could not load the Host USB Proxy Service (%Vrc). "
1796 "The service might be not installed on the host computer"),
1797 mUSBProxyService->getLastError());
1798 if (mUSBProxyService->getLastError() == VINF_SUCCESS)
1799 return setWarning (E_FAIL,
1800 tr ("The USB Proxy Service has not yet been ported to this host"));
1801 return setWarning (E_FAIL,
1802 tr ("Could not load the Host USB Proxy service (%Vrc)"),
1803 mUSBProxyService->getLastError());
1804 }
1805
1806 return S_OK;
1807}
1808#endif /* VBOX_WITH_USB */
1809
1810#ifdef RT_OS_WINDOWS
1811
1812/* The original source of the VBoxTAP adapter creation/destruction code has the following copyright */
1813/*
1814 Copyright 2004 by the Massachusetts Institute of Technology
1815
1816 All rights reserved.
1817
1818 Permission to use, copy, modify, and distribute this software and its
1819 documentation for any purpose and without fee is hereby granted,
1820 provided that the above copyright notice appear in all copies and that
1821 both that copyright notice and this permission notice appear in
1822 supporting documentation, and that the name of the Massachusetts
1823 Institute of Technology (M.I.T.) not be used in advertising or publicity
1824 pertaining to distribution of the software without specific, written
1825 prior permission.
1826
1827 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
1828 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
1829 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
1830 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
1831 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
1832 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
1833 SOFTWARE.
1834*/
1835
1836
1837#define NETSHELL_LIBRARY _T("netshell.dll")
1838
1839/**
1840 * Use the IShellFolder API to rename the connection.
1841 */
1842static HRESULT rename_shellfolder (PCWSTR wGuid, PCWSTR wNewName)
1843{
1844 /* This is the GUID for the network connections folder. It is constant.
1845 * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */
1846 const GUID CLSID_NetworkConnections = {
1847 0x7007ACC7, 0x3202, 0x11D1, {
1848 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
1849 }
1850 };
1851
1852 LPITEMIDLIST pidl = NULL;
1853 IShellFolder *pShellFolder = NULL;
1854 HRESULT hr;
1855
1856 /* Build the display name in the form "::{GUID}". */
1857 if (wcslen (wGuid) >= MAX_PATH)
1858 return E_INVALIDARG;
1859 WCHAR szAdapterGuid[MAX_PATH + 2] = {0};
1860 swprintf (szAdapterGuid, L"::%ls", wGuid);
1861
1862 /* Create an instance of the network connections folder. */
1863 hr = CoCreateInstance (CLSID_NetworkConnections, NULL,
1864 CLSCTX_INPROC_SERVER, IID_IShellFolder,
1865 reinterpret_cast <LPVOID *> (&pShellFolder));
1866 /* Parse the display name. */
1867 if (SUCCEEDED (hr))
1868 {
1869 hr = pShellFolder->ParseDisplayName (NULL, NULL, szAdapterGuid, NULL,
1870 &pidl, NULL);
1871 }
1872 if (SUCCEEDED (hr))
1873 {
1874 hr = pShellFolder->SetNameOf (NULL, pidl, wNewName, SHGDN_NORMAL,
1875 &pidl);
1876 }
1877
1878 CoTaskMemFree (pidl);
1879
1880 if (pShellFolder)
1881 pShellFolder->Release();
1882
1883 return hr;
1884}
1885
1886extern "C" HRESULT RenameConnection (PCWSTR GuidString, PCWSTR NewName)
1887{
1888 typedef HRESULT (WINAPI *lpHrRenameConnection) (const GUID *, PCWSTR);
1889 lpHrRenameConnection RenameConnectionFunc = NULL;
1890 HRESULT status;
1891
1892 /* First try the IShellFolder interface, which was unimplemented
1893 * for the network connections folder before XP. */
1894 status = rename_shellfolder (GuidString, NewName);
1895 if (status == E_NOTIMPL)
1896 {
1897/** @todo that code doesn't seem to work! */
1898 /* The IShellFolder interface is not implemented on this platform.
1899 * Try the (undocumented) HrRenameConnection API in the netshell
1900 * library. */
1901 CLSID clsid;
1902 HINSTANCE hNetShell;
1903 status = CLSIDFromString ((LPOLESTR) GuidString, &clsid);
1904 if (FAILED(status))
1905 return E_FAIL;
1906 hNetShell = LoadLibrary (NETSHELL_LIBRARY);
1907 if (hNetShell == NULL)
1908 return E_FAIL;
1909 RenameConnectionFunc =
1910 (lpHrRenameConnection) GetProcAddress (hNetShell,
1911 "HrRenameConnection");
1912 if (RenameConnectionFunc == NULL)
1913 {
1914 FreeLibrary (hNetShell);
1915 return E_FAIL;
1916 }
1917 status = RenameConnectionFunc (&clsid, NewName);
1918 FreeLibrary (hNetShell);
1919 }
1920 if (FAILED (status))
1921 return status;
1922
1923 return S_OK;
1924}
1925
1926#define DRIVERHWID _T("vboxtap")
1927
1928#define SetErrBreak(strAndArgs) \
1929 if (1) { \
1930 aErrMsg = Utf8StrFmt strAndArgs; vrc = VERR_GENERAL_FAILURE; break; \
1931 } else do {} while (0)
1932
1933/* static */
1934int Host::createNetworkInterface (SVCHlpClient *aClient,
1935 const Utf8Str &aName,
1936 Guid &aGUID, Utf8Str &aErrMsg)
1937{
1938 LogFlowFuncEnter();
1939 LogFlowFunc (("Network connection name = '%s'\n", aName.raw()));
1940
1941 AssertReturn (aClient, VERR_INVALID_POINTER);
1942 AssertReturn (!aName.isNull(), VERR_INVALID_PARAMETER);
1943
1944 int vrc = VINF_SUCCESS;
1945
1946 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
1947 SP_DEVINFO_DATA DeviceInfoData;
1948 DWORD ret = 0;
1949 BOOL found = FALSE;
1950 BOOL registered = FALSE;
1951 BOOL destroyList = FALSE;
1952 TCHAR pCfgGuidString [50];
1953
1954 do
1955 {
1956 BOOL ok;
1957 GUID netGuid;
1958 SP_DRVINFO_DATA DriverInfoData;
1959 SP_DEVINSTALL_PARAMS DeviceInstallParams;
1960 TCHAR className [MAX_PATH];
1961 DWORD index = 0;
1962 PSP_DRVINFO_DETAIL_DATA pDriverInfoDetail;
1963 /* for our purposes, 2k buffer is more
1964 * than enough to obtain the hardware ID
1965 * of the VBoxTAP driver. */
1966 DWORD detailBuf [2048];
1967
1968 HKEY hkey = NULL;
1969 DWORD cbSize;
1970 DWORD dwValueType;
1971
1972 /* initialize the structure size */
1973 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1974 DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
1975
1976 /* copy the net class GUID */
1977 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
1978
1979 /* create an empty device info set associated with the net class GUID */
1980 hDeviceInfo = SetupDiCreateDeviceInfoList (&netGuid, NULL);
1981 if (hDeviceInfo == INVALID_HANDLE_VALUE)
1982 SetErrBreak (("SetupDiCreateDeviceInfoList failed (0x%08X)",
1983 GetLastError()));
1984
1985 /* get the class name from GUID */
1986 ok = SetupDiClassNameFromGuid (&netGuid, className, MAX_PATH, NULL);
1987 if (!ok)
1988 SetErrBreak (("SetupDiClassNameFromGuid failed (0x%08X)",
1989 GetLastError()));
1990
1991 /* create a device info element and add the new device instance
1992 * key to registry */
1993 ok = SetupDiCreateDeviceInfo (hDeviceInfo, className, &netGuid, NULL, NULL,
1994 DICD_GENERATE_ID, &DeviceInfoData);
1995 if (!ok)
1996 SetErrBreak (("SetupDiCreateDeviceInfo failed (0x%08X)",
1997 GetLastError()));
1998
1999 /* select the newly created device info to be the currently
2000 selected member */
2001 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2002 if (!ok)
2003 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2004 GetLastError()));
2005
2006 /* build a list of class drivers */
2007 ok = SetupDiBuildDriverInfoList (hDeviceInfo, &DeviceInfoData,
2008 SPDIT_CLASSDRIVER);
2009 if (!ok)
2010 SetErrBreak (("SetupDiBuildDriverInfoList failed (0x%08X)",
2011 GetLastError()));
2012
2013 destroyList = TRUE;
2014
2015 /* enumerate the driver info list */
2016 while (TRUE)
2017 {
2018 BOOL ret;
2019
2020 ret = SetupDiEnumDriverInfo (hDeviceInfo, &DeviceInfoData,
2021 SPDIT_CLASSDRIVER, index, &DriverInfoData);
2022
2023 /* if the function failed and GetLastError() returned
2024 * ERROR_NO_MORE_ITEMS, then we have reached the end of the
2025 * list. Othewise there was something wrong with this
2026 * particular driver. */
2027 if (!ret)
2028 {
2029 if(GetLastError() == ERROR_NO_MORE_ITEMS)
2030 break;
2031 else
2032 {
2033 index++;
2034 continue;
2035 }
2036 }
2037
2038 pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA) detailBuf;
2039 pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
2040
2041 /* if we successfully find the hardware ID and it turns out to
2042 * be the one for the loopback driver, then we are done. */
2043 if (SetupDiGetDriverInfoDetail (hDeviceInfo,
2044 &DeviceInfoData,
2045 &DriverInfoData,
2046 pDriverInfoDetail,
2047 sizeof (detailBuf),
2048 NULL))
2049 {
2050 TCHAR * t;
2051
2052 /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the
2053 * whole list and see if there is a match somewhere. */
2054 t = pDriverInfoDetail->HardwareID;
2055 while (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2056 {
2057 if (!_tcsicmp(t, DRIVERHWID))
2058 break;
2059
2060 t += _tcslen(t) + 1;
2061 }
2062
2063 if (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2064 {
2065 found = TRUE;
2066 break;
2067 }
2068 }
2069
2070 index ++;
2071 }
2072
2073 if (!found)
2074 SetErrBreak ((tr ("Could not find Host Interface Networking driver! "
2075 "Please reinstall")));
2076
2077 /* set the loopback driver to be the currently selected */
2078 ok = SetupDiSetSelectedDriver (hDeviceInfo, &DeviceInfoData,
2079 &DriverInfoData);
2080 if (!ok)
2081 SetErrBreak (("SetupDiSetSelectedDriver failed (0x%08X)",
2082 GetLastError()));
2083
2084 /* register the phantom device to prepare for install */
2085 ok = SetupDiCallClassInstaller (DIF_REGISTERDEVICE, hDeviceInfo,
2086 &DeviceInfoData);
2087 if (!ok)
2088 SetErrBreak (("SetupDiCallClassInstaller failed (0x%08X)",
2089 GetLastError()));
2090
2091 /* registered, but remove if errors occur in the following code */
2092 registered = TRUE;
2093
2094 /* ask the installer if we can install the device */
2095 ok = SetupDiCallClassInstaller (DIF_ALLOW_INSTALL, hDeviceInfo,
2096 &DeviceInfoData);
2097 if (!ok)
2098 {
2099 if (GetLastError() != ERROR_DI_DO_DEFAULT)
2100 SetErrBreak (("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (0x%08X)",
2101 GetLastError()));
2102 /* that's fine */
2103 }
2104
2105 /* install the files first */
2106 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES, hDeviceInfo,
2107 &DeviceInfoData);
2108 if (!ok)
2109 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (0x%08X)",
2110 GetLastError()));
2111
2112 /* get the device install parameters and disable filecopy */
2113 DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
2114 ok = SetupDiGetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2115 &DeviceInstallParams);
2116 if (ok)
2117 {
2118 DeviceInstallParams.Flags |= DI_NOFILECOPY;
2119 ok = SetupDiSetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2120 &DeviceInstallParams);
2121 if (!ok)
2122 SetErrBreak (("SetupDiSetDeviceInstallParams failed (0x%08X)",
2123 GetLastError()));
2124 }
2125
2126 /*
2127 * Register any device-specific co-installers for this device,
2128 */
2129
2130 ok = SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS,
2131 hDeviceInfo,
2132 &DeviceInfoData);
2133 if (!ok)
2134 SetErrBreak (("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (0x%08X)",
2135 GetLastError()));
2136
2137 /*
2138 * install any installer-specified interfaces.
2139 * and then do the real install
2140 */
2141 ok = SetupDiCallClassInstaller (DIF_INSTALLINTERFACES,
2142 hDeviceInfo,
2143 &DeviceInfoData);
2144 if (!ok)
2145 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (0x%08X)",
2146 GetLastError()));
2147
2148 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICE,
2149 hDeviceInfo,
2150 &DeviceInfoData);
2151 if (!ok)
2152 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (0x%08X)",
2153 GetLastError()));
2154
2155 /* Figure out NetCfgInstanceId */
2156 hkey = SetupDiOpenDevRegKey (hDeviceInfo,
2157 &DeviceInfoData,
2158 DICS_FLAG_GLOBAL,
2159 0,
2160 DIREG_DRV,
2161 KEY_READ);
2162 if (hkey == INVALID_HANDLE_VALUE)
2163 SetErrBreak (("SetupDiOpenDevRegKey failed (0x%08X)",
2164 GetLastError()));
2165
2166 cbSize = sizeof (pCfgGuidString);
2167 DWORD ret;
2168 ret = RegQueryValueEx (hkey, _T ("NetCfgInstanceId"), NULL,
2169 &dwValueType, (LPBYTE) pCfgGuidString, &cbSize);
2170 RegCloseKey (hkey);
2171
2172 ret = RenameConnection (pCfgGuidString, Bstr (aName));
2173 if (FAILED (ret))
2174 SetErrBreak (("Failed to set interface name (ret=0x%08X, "
2175 "pCfgGuidString='%ls', cbSize=%d)",
2176 ret, pCfgGuidString, cbSize));
2177 }
2178 while (0);
2179
2180 /*
2181 * cleanup
2182 */
2183
2184 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2185 {
2186 /* an error has occured, but the device is registered, we must remove it */
2187 if (ret != 0 && registered)
2188 SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2189
2190 found = SetupDiDeleteDeviceInfo (hDeviceInfo, &DeviceInfoData);
2191
2192 /* destroy the driver info list */
2193 if (destroyList)
2194 SetupDiDestroyDriverInfoList (hDeviceInfo, &DeviceInfoData,
2195 SPDIT_CLASSDRIVER);
2196 /* clean up the device info set */
2197 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2198 }
2199
2200 /* return the network connection GUID on success */
2201 if (VBOX_SUCCESS (vrc))
2202 {
2203 /* remove the curly bracket at the end */
2204 pCfgGuidString [_tcslen (pCfgGuidString) - 1] = '\0';
2205 LogFlowFunc (("Network connection GUID string = {%ls}\n", pCfgGuidString + 1));
2206
2207 aGUID = Guid (Utf8Str (pCfgGuidString + 1));
2208 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2209 Assert (!aGUID.isEmpty());
2210 }
2211
2212 LogFlowFunc (("vrc=%Vrc\n", vrc));
2213 LogFlowFuncLeave();
2214 return vrc;
2215}
2216
2217/* static */
2218int Host::removeNetworkInterface (SVCHlpClient *aClient,
2219 const Guid &aGUID,
2220 Utf8Str &aErrMsg)
2221{
2222 LogFlowFuncEnter();
2223 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2224
2225 AssertReturn (aClient, VERR_INVALID_POINTER);
2226 AssertReturn (!aGUID.isEmpty(), VERR_INVALID_PARAMETER);
2227
2228 int vrc = VINF_SUCCESS;
2229
2230 do
2231 {
2232 TCHAR lszPnPInstanceId [512] = {0};
2233
2234 /* We have to find the device instance ID through a registry search */
2235
2236 HKEY hkeyNetwork = 0;
2237 HKEY hkeyConnection = 0;
2238
2239 do
2240 {
2241 char strRegLocation [256];
2242 sprintf (strRegLocation,
2243 "SYSTEM\\CurrentControlSet\\Control\\Network\\"
2244 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\{%s}",
2245 aGUID.toString().raw());
2246 LONG status;
2247 status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, strRegLocation, 0,
2248 KEY_READ, &hkeyNetwork);
2249 if ((status != ERROR_SUCCESS) || !hkeyNetwork)
2250 SetErrBreak ((
2251 tr ("Host interface network is not found in registry (%s) [1]"),
2252 strRegLocation));
2253
2254 status = RegOpenKeyExA (hkeyNetwork, "Connection", 0,
2255 KEY_READ, &hkeyConnection);
2256 if ((status != ERROR_SUCCESS) || !hkeyConnection)
2257 SetErrBreak ((
2258 tr ("Host interface network is not found in registry (%s) [2]"),
2259 strRegLocation));
2260
2261 DWORD len = sizeof (lszPnPInstanceId);
2262 DWORD dwKeyType;
2263 status = RegQueryValueExW (hkeyConnection, L"PnPInstanceID", NULL,
2264 &dwKeyType, (LPBYTE) lszPnPInstanceId, &len);
2265 if ((status != ERROR_SUCCESS) || (dwKeyType != REG_SZ))
2266 SetErrBreak ((
2267 tr ("Host interface network is not found in registry (%s) [3]"),
2268 strRegLocation));
2269 }
2270 while (0);
2271
2272 if (hkeyConnection)
2273 RegCloseKey (hkeyConnection);
2274 if (hkeyNetwork)
2275 RegCloseKey (hkeyNetwork);
2276
2277 if (VBOX_FAILURE (vrc))
2278 break;
2279
2280 /*
2281 * Now we are going to enumerate all network devices and
2282 * wait until we encounter the right device instance ID
2283 */
2284
2285 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2286
2287 do
2288 {
2289 BOOL ok;
2290 DWORD ret = 0;
2291 GUID netGuid;
2292 SP_DEVINFO_DATA DeviceInfoData;
2293 DWORD index = 0;
2294 BOOL found = FALSE;
2295 DWORD size = 0;
2296
2297 /* initialize the structure size */
2298 DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
2299
2300 /* copy the net class GUID */
2301 memcpy (&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2302
2303 /* return a device info set contains all installed devices of the Net class */
2304 hDeviceInfo = SetupDiGetClassDevs (&netGuid, NULL, NULL, DIGCF_PRESENT);
2305
2306 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2307 SetErrBreak (("SetupDiGetClassDevs failed (0x%08X)", GetLastError()));
2308
2309 /* enumerate the driver info list */
2310 while (TRUE)
2311 {
2312 TCHAR *deviceHwid;
2313
2314 ok = SetupDiEnumDeviceInfo (hDeviceInfo, index, &DeviceInfoData);
2315
2316 if (!ok)
2317 {
2318 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2319 break;
2320 else
2321 {
2322 index++;
2323 continue;
2324 }
2325 }
2326
2327 /* try to get the hardware ID registry property */
2328 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2329 &DeviceInfoData,
2330 SPDRP_HARDWAREID,
2331 NULL,
2332 NULL,
2333 0,
2334 &size);
2335 if (!ok)
2336 {
2337 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2338 {
2339 index++;
2340 continue;
2341 }
2342
2343 deviceHwid = (TCHAR *) malloc (size);
2344 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2345 &DeviceInfoData,
2346 SPDRP_HARDWAREID,
2347 NULL,
2348 (PBYTE)deviceHwid,
2349 size,
2350 NULL);
2351 if (!ok)
2352 {
2353 free (deviceHwid);
2354 deviceHwid = NULL;
2355 index++;
2356 continue;
2357 }
2358 }
2359 else
2360 {
2361 /* something is wrong. This shouldn't have worked with a NULL buffer */
2362 index++;
2363 continue;
2364 }
2365
2366 for (TCHAR *t = deviceHwid;
2367 t && *t && t < &deviceHwid[size / sizeof(TCHAR)];
2368 t += _tcslen (t) + 1)
2369 {
2370 if (!_tcsicmp (DRIVERHWID, t))
2371 {
2372 /* get the device instance ID */
2373 TCHAR devID [MAX_DEVICE_ID_LEN];
2374 if (CM_Get_Device_ID(DeviceInfoData.DevInst,
2375 devID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2376 {
2377 /* compare to what we determined before */
2378 if (wcscmp(devID, lszPnPInstanceId) == 0)
2379 {
2380 found = TRUE;
2381 break;
2382 }
2383 }
2384 }
2385 }
2386
2387 if (deviceHwid)
2388 {
2389 free (deviceHwid);
2390 deviceHwid = NULL;
2391 }
2392
2393 if (found)
2394 break;
2395
2396 index++;
2397 }
2398
2399 if (found == FALSE)
2400 SetErrBreak ((tr ("Host Interface Network driver not found (0x%08X)"),
2401 GetLastError()));
2402
2403 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2404 if (!ok)
2405 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2406 GetLastError()));
2407
2408 ok = SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2409 if (!ok)
2410 SetErrBreak (("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)",
2411 GetLastError()));
2412 }
2413 while (0);
2414
2415 /* clean up the device info set */
2416 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2417 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2418
2419 if (VBOX_FAILURE (vrc))
2420 break;
2421 }
2422 while (0);
2423
2424 LogFlowFunc (("vrc=%Vrc\n", vrc));
2425 LogFlowFuncLeave();
2426 return vrc;
2427}
2428
2429#undef SetErrBreak
2430
2431/* static */
2432HRESULT Host::networkInterfaceHelperClient (SVCHlpClient *aClient,
2433 Progress *aProgress,
2434 void *aUser, int *aVrc)
2435{
2436 LogFlowFuncEnter();
2437 LogFlowFunc (("aClient={%p}, aProgress={%p}, aUser={%p}\n",
2438 aClient, aProgress, aUser));
2439
2440 AssertReturn ((aClient == NULL && aProgress == NULL && aVrc == NULL) ||
2441 (aClient != NULL && aProgress != NULL && aVrc != NULL),
2442 E_POINTER);
2443 AssertReturn (aUser, E_POINTER);
2444
2445 std::auto_ptr <NetworkInterfaceHelperClientData>
2446 d (static_cast <NetworkInterfaceHelperClientData *> (aUser));
2447
2448 if (aClient == NULL)
2449 {
2450 /* "cleanup only" mode, just return (it will free aUser) */
2451 return S_OK;
2452 }
2453
2454 HRESULT rc = S_OK;
2455 int vrc = VINF_SUCCESS;
2456
2457 switch (d->msgCode)
2458 {
2459 case SVCHlpMsg::CreateHostNetworkInterface:
2460 {
2461 LogFlowFunc (("CreateHostNetworkInterface:\n"));
2462 LogFlowFunc (("Network connection name = '%ls'\n", d->name.raw()));
2463
2464 /* write message and parameters */
2465 vrc = aClient->write (d->msgCode);
2466 if (VBOX_FAILURE (vrc)) break;
2467 vrc = aClient->write (Utf8Str (d->name));
2468 if (VBOX_FAILURE (vrc)) break;
2469
2470 /* wait for a reply */
2471 bool endLoop = false;
2472 while (!endLoop)
2473 {
2474 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
2475
2476 vrc = aClient->read (reply);
2477 if (VBOX_FAILURE (vrc)) break;
2478
2479 switch (reply)
2480 {
2481 case SVCHlpMsg::CreateHostNetworkInterface_OK:
2482 {
2483 /* read the GUID */
2484 Guid guid;
2485 vrc = aClient->read (guid);
2486 if (VBOX_FAILURE (vrc)) break;
2487
2488 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", guid.raw()));
2489
2490 /* initialize the object returned to the caller by
2491 * CreateHostNetworkInterface() */
2492 rc = d->iface->init (d->name, guid);
2493 endLoop = true;
2494 break;
2495 }
2496 case SVCHlpMsg::Error:
2497 {
2498 /* read the error message */
2499 Utf8Str errMsg;
2500 vrc = aClient->read (errMsg);
2501 if (VBOX_FAILURE (vrc)) break;
2502
2503 rc = setError (E_FAIL, errMsg);
2504 endLoop = true;
2505 break;
2506 }
2507 default:
2508 {
2509 endLoop = true;
2510 ComAssertMsgFailedBreak ((
2511 "Invalid message code %d (%08lX)\n",
2512 reply, reply),
2513 rc = E_FAIL);
2514 }
2515 }
2516 }
2517
2518 break;
2519 }
2520 case SVCHlpMsg::RemoveHostNetworkInterface:
2521 {
2522 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
2523 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", d->guid.raw()));
2524
2525 /* write message and parameters */
2526 vrc = aClient->write (d->msgCode);
2527 if (VBOX_FAILURE (vrc)) break;
2528 vrc = aClient->write (d->guid);
2529 if (VBOX_FAILURE (vrc)) break;
2530
2531 /* wait for a reply */
2532 bool endLoop = false;
2533 while (!endLoop)
2534 {
2535 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
2536
2537 vrc = aClient->read (reply);
2538 if (VBOX_FAILURE (vrc)) break;
2539
2540 switch (reply)
2541 {
2542 case SVCHlpMsg::OK:
2543 {
2544 /* no parameters */
2545 rc = S_OK;
2546 endLoop = true;
2547 break;
2548 }
2549 case SVCHlpMsg::Error:
2550 {
2551 /* read the error message */
2552 Utf8Str errMsg;
2553 vrc = aClient->read (errMsg);
2554 if (VBOX_FAILURE (vrc)) break;
2555
2556 rc = setError (E_FAIL, errMsg);
2557 endLoop = true;
2558 break;
2559 }
2560 default:
2561 {
2562 endLoop = true;
2563 ComAssertMsgFailedBreak ((
2564 "Invalid message code %d (%08lX)\n",
2565 reply, reply),
2566 rc = E_FAIL);
2567 }
2568 }
2569 }
2570
2571 break;
2572 }
2573 default:
2574 ComAssertMsgFailedBreak ((
2575 "Invalid message code %d (%08lX)\n",
2576 d->msgCode, d->msgCode),
2577 rc = E_FAIL);
2578 }
2579
2580 if (aVrc)
2581 *aVrc = vrc;
2582
2583 LogFlowFunc (("rc=0x%08X, vrc=%Vrc\n", rc, vrc));
2584 LogFlowFuncLeave();
2585 return rc;
2586}
2587
2588/* static */
2589int Host::networkInterfaceHelperServer (SVCHlpClient *aClient,
2590 SVCHlpMsg::Code aMsgCode)
2591{
2592 LogFlowFuncEnter();
2593 LogFlowFunc (("aClient={%p}, aMsgCode=%d\n", aClient, aMsgCode));
2594
2595 AssertReturn (aClient, VERR_INVALID_POINTER);
2596
2597 int vrc = VINF_SUCCESS;
2598
2599 switch (aMsgCode)
2600 {
2601 case SVCHlpMsg::CreateHostNetworkInterface:
2602 {
2603 LogFlowFunc (("CreateHostNetworkInterface:\n"));
2604
2605 Utf8Str name;
2606 vrc = aClient->read (name);
2607 if (VBOX_FAILURE (vrc)) break;
2608
2609 Guid guid;
2610 Utf8Str errMsg;
2611 vrc = createNetworkInterface (aClient, name, guid, errMsg);
2612
2613 if (VBOX_SUCCESS (vrc))
2614 {
2615 /* write success followed by GUID */
2616 vrc = aClient->write (SVCHlpMsg::CreateHostNetworkInterface_OK);
2617 if (VBOX_FAILURE (vrc)) break;
2618 vrc = aClient->write (guid);
2619 if (VBOX_FAILURE (vrc)) break;
2620 }
2621 else
2622 {
2623 /* write failure followed by error message */
2624 if (errMsg.isEmpty())
2625 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
2626 vrc = aClient->write (SVCHlpMsg::Error);
2627 if (VBOX_FAILURE (vrc)) break;
2628 vrc = aClient->write (errMsg);
2629 if (VBOX_FAILURE (vrc)) break;
2630 }
2631
2632 break;
2633 }
2634 case SVCHlpMsg::RemoveHostNetworkInterface:
2635 {
2636 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
2637
2638 Guid guid;
2639 vrc = aClient->read (guid);
2640 if (VBOX_FAILURE (vrc)) break;
2641
2642 Utf8Str errMsg;
2643 vrc = removeNetworkInterface (aClient, guid, errMsg);
2644
2645 if (VBOX_SUCCESS (vrc))
2646 {
2647 /* write parameter-less success */
2648 vrc = aClient->write (SVCHlpMsg::OK);
2649 if (VBOX_FAILURE (vrc)) break;
2650 }
2651 else
2652 {
2653 /* write failure followed by error message */
2654 if (errMsg.isEmpty())
2655 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
2656 vrc = aClient->write (SVCHlpMsg::Error);
2657 if (VBOX_FAILURE (vrc)) break;
2658 vrc = aClient->write (errMsg);
2659 if (VBOX_FAILURE (vrc)) break;
2660 }
2661
2662 break;
2663 }
2664 default:
2665 AssertMsgFailedBreakStmt (
2666 ("Invalid message code %d (%08lX)\n", aMsgCode, aMsgCode),
2667 VERR_GENERAL_FAILURE);
2668 }
2669
2670 LogFlowFunc (("vrc=%Vrc\n", vrc));
2671 LogFlowFuncLeave();
2672 return vrc;
2673}
2674
2675#endif /* RT_OS_WINDOWS */
2676
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