VirtualBox

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

Last change on this file since 3662 was 3578, checked in by vboxsync, 17 years ago

Removed rendermode property from the base IFrameBuffer class

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