VirtualBox

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

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

(re-)run filters when the device becomes available (again).

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