VirtualBox

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

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

More USBFILTER changes.

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