VirtualBox

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

Last change on this file since 3566 was 3566, checked in by vboxsync, 18 years ago

Async USB detach (for darwin) with async operation timeout (not enabled on windows). Drop the USBProxyService::reset method. (Hope I didn't break anything, it's only tested on Darwin...)

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

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