VirtualBox

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

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

Main: Fixed async USB (added proper detection of device re-cycle on Win32; changed manual release sequence so that a detach is made in VMM first, the USB proxy is notified afterwards on success; some other small fixes).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 99.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 __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
2085 {
2086 /* some external state change */
2087
2088 /// @todo re-run all USB filters probably
2089 AssertFailed();
2090 }
2091}
2092
2093/**
2094 * Checks for the presense and status of the USB Proxy Service.
2095 * Returns S_OK when the Proxy is present and OK, or E_FAIL and a
2096 * corresponding error message otherwise. Intended to be used by methods
2097 * that rely on the Proxy Service availability.
2098 *
2099 * @note Locks this object for reading.
2100 */
2101HRESULT Host::checkUSBProxyService()
2102{
2103#ifdef VBOX_WITH_USB
2104 AutoLock lock (this);
2105 CHECK_READY();
2106
2107 AssertReturn (mUSBProxyService, E_FAIL);
2108 if (!mUSBProxyService->isActive())
2109 {
2110 /* disable the USB controller completely to avoid assertions if the
2111 * USB proxy service could not start. */
2112
2113 Assert (VBOX_FAILURE (mUSBProxyService->getLastError()));
2114 if (mUSBProxyService->getLastError() == VERR_FILE_NOT_FOUND)
2115 return setError (E_FAIL,
2116 tr ("Could not load the Host USB Proxy Service (%Vrc)."
2117 "The service might be not installed on the host computer"),
2118 mUSBProxyService->getLastError());
2119 else
2120 return setError (E_FAIL,
2121 tr ("Could not load the Host USB Proxy service (%Vrc)"),
2122 mUSBProxyService->getLastError());
2123 }
2124
2125 return S_OK;
2126#else
2127 return E_NOTIMPL;
2128#endif
2129}
2130
2131#ifdef __WIN__
2132
2133/* The original source of the VBoxTAP adapter creation/destruction code has the following copyright */
2134/*
2135 Copyright 2004 by the Massachusetts Institute of Technology
2136
2137 All rights reserved.
2138
2139 Permission to use, copy, modify, and distribute this software and its
2140 documentation for any purpose and without fee is hereby granted,
2141 provided that the above copyright notice appear in all copies and that
2142 both that copyright notice and this permission notice appear in
2143 supporting documentation, and that the name of the Massachusetts
2144 Institute of Technology (M.I.T.) not be used in advertising or publicity
2145 pertaining to distribution of the software without specific, written
2146 prior permission.
2147
2148 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
2149 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
2150 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
2151 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
2152 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2153 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2154 SOFTWARE.
2155*/
2156
2157
2158#define NETSHELL_LIBRARY _T("netshell.dll")
2159
2160/**
2161 * Use the IShellFolder API to rename the connection.
2162 */
2163static HRESULT rename_shellfolder (PCWSTR wGuid, PCWSTR wNewName)
2164{
2165 /* This is the GUID for the network connections folder. It is constant.
2166 * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */
2167 const GUID CLSID_NetworkConnections = {
2168 0x7007ACC7, 0x3202, 0x11D1, {
2169 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
2170 }
2171 };
2172
2173 LPITEMIDLIST pidl = NULL;
2174 IShellFolder *pShellFolder = NULL;
2175 HRESULT hr;
2176
2177 /* Build the display name in the form "::{GUID}". */
2178 if (wcslen (wGuid) >= MAX_PATH)
2179 return E_INVALIDARG;
2180 WCHAR szAdapterGuid[MAX_PATH + 2] = {0};
2181 swprintf (szAdapterGuid, L"::%ls", wGuid);
2182
2183 /* Create an instance of the network connections folder. */
2184 hr = CoCreateInstance (CLSID_NetworkConnections, NULL,
2185 CLSCTX_INPROC_SERVER, IID_IShellFolder,
2186 reinterpret_cast <LPVOID *> (&pShellFolder));
2187 /* Parse the display name. */
2188 if (SUCCEEDED (hr))
2189 {
2190 hr = pShellFolder->ParseDisplayName (NULL, NULL, szAdapterGuid, NULL,
2191 &pidl, NULL);
2192 }
2193 if (SUCCEEDED (hr))
2194 {
2195 hr = pShellFolder->SetNameOf (NULL, pidl, wNewName, SHGDN_NORMAL,
2196 &pidl);
2197 }
2198
2199 CoTaskMemFree (pidl);
2200
2201 if (pShellFolder)
2202 pShellFolder->Release();
2203
2204 return hr;
2205}
2206
2207extern "C" HRESULT RenameConnection (PCWSTR GuidString, PCWSTR NewName)
2208{
2209 typedef HRESULT (WINAPI *lpHrRenameConnection) (const GUID *, PCWSTR);
2210 lpHrRenameConnection RenameConnectionFunc = NULL;
2211 HRESULT status;
2212
2213 /* First try the IShellFolder interface, which was unimplemented
2214 * for the network connections folder before XP. */
2215 status = rename_shellfolder (GuidString, NewName);
2216 if (status == E_NOTIMPL)
2217 {
2218/** @todo that code doesn't seem to work! */
2219 /* The IShellFolder interface is not implemented on this platform.
2220 * Try the (undocumented) HrRenameConnection API in the netshell
2221 * library. */
2222 CLSID clsid;
2223 HINSTANCE hNetShell;
2224 status = CLSIDFromString ((LPOLESTR) GuidString, &clsid);
2225 if (FAILED(status))
2226 return E_FAIL;
2227 hNetShell = LoadLibrary (NETSHELL_LIBRARY);
2228 if (hNetShell == NULL)
2229 return E_FAIL;
2230 RenameConnectionFunc =
2231 (lpHrRenameConnection) GetProcAddress (hNetShell,
2232 "HrRenameConnection");
2233 if (RenameConnectionFunc == NULL)
2234 {
2235 FreeLibrary (hNetShell);
2236 return E_FAIL;
2237 }
2238 status = RenameConnectionFunc (&clsid, NewName);
2239 FreeLibrary (hNetShell);
2240 }
2241 if (FAILED (status))
2242 return status;
2243
2244 return S_OK;
2245}
2246
2247#define DRIVERHWID _T("vboxtap")
2248
2249#define SetErrBreak(strAndArgs) \
2250 if (1) { \
2251 aErrMsg = Utf8StrFmt strAndArgs; vrc = VERR_GENERAL_FAILURE; break; \
2252 } else do {} while (0)
2253
2254/* static */
2255int Host::createNetworkInterface (SVCHlpClient *aClient,
2256 const Utf8Str &aName,
2257 Guid &aGUID, Utf8Str &aErrMsg)
2258{
2259 LogFlowFuncEnter();
2260 LogFlowFunc (("Network connection name = '%s'\n", aName.raw()));
2261
2262 AssertReturn (aClient, VERR_INVALID_POINTER);
2263 AssertReturn (!aName.isNull(), VERR_INVALID_PARAMETER);
2264
2265 int vrc = VINF_SUCCESS;
2266
2267 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2268 SP_DEVINFO_DATA DeviceInfoData;
2269 DWORD ret = 0;
2270 BOOL found = FALSE;
2271 BOOL registered = FALSE;
2272 BOOL destroyList = FALSE;
2273 TCHAR pCfgGuidString [50];
2274
2275 do
2276 {
2277 BOOL ok;
2278 GUID netGuid;
2279 SP_DRVINFO_DATA DriverInfoData;
2280 SP_DEVINSTALL_PARAMS DeviceInstallParams;
2281 TCHAR className [MAX_PATH];
2282 DWORD index = 0;
2283 PSP_DRVINFO_DETAIL_DATA pDriverInfoDetail;
2284 /* for our purposes, 2k buffer is more
2285 * than enough to obtain the hardware ID
2286 * of the VBoxTAP driver. */
2287 DWORD detailBuf [2048];
2288
2289 HKEY hkey = NULL;
2290 DWORD cbSize;
2291 DWORD dwValueType;
2292
2293 /* initialize the structure size */
2294 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
2295 DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
2296
2297 /* copy the net class GUID */
2298 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
2299
2300 /* create an empty device info set associated with the net class GUID */
2301 hDeviceInfo = SetupDiCreateDeviceInfoList (&netGuid, NULL);
2302 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2303 SetErrBreak (("SetupDiCreateDeviceInfoList failed (0x%08X)",
2304 GetLastError()));
2305
2306 /* get the class name from GUID */
2307 ok = SetupDiClassNameFromGuid (&netGuid, className, MAX_PATH, NULL);
2308 if (!ok)
2309 SetErrBreak (("SetupDiClassNameFromGuid failed (0x%08X)",
2310 GetLastError()));
2311
2312 /* create a device info element and add the new device instance
2313 * key to registry */
2314 ok = SetupDiCreateDeviceInfo (hDeviceInfo, className, &netGuid, NULL, NULL,
2315 DICD_GENERATE_ID, &DeviceInfoData);
2316 if (!ok)
2317 SetErrBreak (("SetupDiCreateDeviceInfo failed (0x%08X)",
2318 GetLastError()));
2319
2320 /* select the newly created device info to be the currently
2321 selected member */
2322 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2323 if (!ok)
2324 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2325 GetLastError()));
2326
2327 /* build a list of class drivers */
2328 ok = SetupDiBuildDriverInfoList (hDeviceInfo, &DeviceInfoData,
2329 SPDIT_CLASSDRIVER);
2330 if (!ok)
2331 SetErrBreak (("SetupDiBuildDriverInfoList failed (0x%08X)",
2332 GetLastError()));
2333
2334 destroyList = TRUE;
2335
2336 /* enumerate the driver info list */
2337 while (TRUE)
2338 {
2339 BOOL ret;
2340
2341 ret = SetupDiEnumDriverInfo (hDeviceInfo, &DeviceInfoData,
2342 SPDIT_CLASSDRIVER, index, &DriverInfoData);
2343
2344 /* if the function failed and GetLastError() returned
2345 * ERROR_NO_MORE_ITEMS, then we have reached the end of the
2346 * list. Othewise there was something wrong with this
2347 * particular driver. */
2348 if (!ret)
2349 {
2350 if(GetLastError() == ERROR_NO_MORE_ITEMS)
2351 break;
2352 else
2353 {
2354 index++;
2355 continue;
2356 }
2357 }
2358
2359 pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA) detailBuf;
2360 pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
2361
2362 /* if we successfully find the hardware ID and it turns out to
2363 * be the one for the loopback driver, then we are done. */
2364 if (SetupDiGetDriverInfoDetail (hDeviceInfo,
2365 &DeviceInfoData,
2366 &DriverInfoData,
2367 pDriverInfoDetail,
2368 sizeof (detailBuf),
2369 NULL))
2370 {
2371 TCHAR * t;
2372
2373 /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the
2374 * whole list and see if there is a match somewhere. */
2375 t = pDriverInfoDetail->HardwareID;
2376 while (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2377 {
2378 if (!_tcsicmp(t, DRIVERHWID))
2379 break;
2380
2381 t += _tcslen(t) + 1;
2382 }
2383
2384 if (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2385 {
2386 found = TRUE;
2387 break;
2388 }
2389 }
2390
2391 index ++;
2392 }
2393
2394 if (!found)
2395 SetErrBreak ((tr ("Could not find Host Interface Networking driver! "
2396 "Please reinstall")));
2397
2398 /* set the loopback driver to be the currently selected */
2399 ok = SetupDiSetSelectedDriver (hDeviceInfo, &DeviceInfoData,
2400 &DriverInfoData);
2401 if (!ok)
2402 SetErrBreak (("SetupDiSetSelectedDriver failed (0x%08X)",
2403 GetLastError()));
2404
2405 /* register the phantom device to prepare for install */
2406 ok = SetupDiCallClassInstaller (DIF_REGISTERDEVICE, hDeviceInfo,
2407 &DeviceInfoData);
2408 if (!ok)
2409 SetErrBreak (("SetupDiCallClassInstaller failed (0x%08X)",
2410 GetLastError()));
2411
2412 /* registered, but remove if errors occur in the following code */
2413 registered = TRUE;
2414
2415 /* ask the installer if we can install the device */
2416 ok = SetupDiCallClassInstaller (DIF_ALLOW_INSTALL, hDeviceInfo,
2417 &DeviceInfoData);
2418 if (!ok)
2419 {
2420 if (GetLastError() != ERROR_DI_DO_DEFAULT)
2421 SetErrBreak (("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (0x%08X)",
2422 GetLastError()));
2423 /* that's fine */
2424 }
2425
2426 /* install the files first */
2427 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES, hDeviceInfo,
2428 &DeviceInfoData);
2429 if (!ok)
2430 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (0x%08X)",
2431 GetLastError()));
2432
2433 /* get the device install parameters and disable filecopy */
2434 DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
2435 ok = SetupDiGetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2436 &DeviceInstallParams);
2437 if (ok)
2438 {
2439 DeviceInstallParams.Flags |= DI_NOFILECOPY;
2440 ok = SetupDiSetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2441 &DeviceInstallParams);
2442 if (!ok)
2443 SetErrBreak (("SetupDiSetDeviceInstallParams failed (0x%08X)",
2444 GetLastError()));
2445 }
2446
2447 /*
2448 * Register any device-specific co-installers for this device,
2449 */
2450
2451 ok = SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS,
2452 hDeviceInfo,
2453 &DeviceInfoData);
2454 if (!ok)
2455 SetErrBreak (("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (0x%08X)",
2456 GetLastError()));
2457
2458 /*
2459 * install any installer-specified interfaces.
2460 * and then do the real install
2461 */
2462 ok = SetupDiCallClassInstaller (DIF_INSTALLINTERFACES,
2463 hDeviceInfo,
2464 &DeviceInfoData);
2465 if (!ok)
2466 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (0x%08X)",
2467 GetLastError()));
2468
2469 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICE,
2470 hDeviceInfo,
2471 &DeviceInfoData);
2472 if (!ok)
2473 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (0x%08X)",
2474 GetLastError()));
2475
2476 /* Figure out NetCfgInstanceId */
2477 hkey = SetupDiOpenDevRegKey (hDeviceInfo,
2478 &DeviceInfoData,
2479 DICS_FLAG_GLOBAL,
2480 0,
2481 DIREG_DRV,
2482 KEY_READ);
2483 if (hkey == INVALID_HANDLE_VALUE)
2484 SetErrBreak (("SetupDiOpenDevRegKey failed (0x%08X)",
2485 GetLastError()));
2486
2487 cbSize = sizeof (pCfgGuidString);
2488 DWORD ret;
2489 ret = RegQueryValueEx (hkey, _T ("NetCfgInstanceId"), NULL,
2490 &dwValueType, (LPBYTE) pCfgGuidString, &cbSize);
2491 RegCloseKey (hkey);
2492
2493 ret = RenameConnection (pCfgGuidString, Bstr (aName));
2494 if (FAILED (ret))
2495 SetErrBreak (("Failed to set interface name (ret=0x%08X, "
2496 "pCfgGuidString='%ls', cbSize=%d)",
2497 ret, pCfgGuidString, cbSize));
2498 }
2499 while (0);
2500
2501 /*
2502 * cleanup
2503 */
2504
2505 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2506 {
2507 /* an error has occured, but the device is registered, we must remove it */
2508 if (ret != 0 && registered)
2509 SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2510
2511 found = SetupDiDeleteDeviceInfo (hDeviceInfo, &DeviceInfoData);
2512
2513 /* destroy the driver info list */
2514 if (destroyList)
2515 SetupDiDestroyDriverInfoList (hDeviceInfo, &DeviceInfoData,
2516 SPDIT_CLASSDRIVER);
2517 /* clean up the device info set */
2518 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2519 }
2520
2521 /* return the network connection GUID on success */
2522 if (VBOX_SUCCESS (vrc))
2523 {
2524 /* remove the curly bracket at the end */
2525 pCfgGuidString [_tcslen (pCfgGuidString) - 1] = '\0';
2526 LogFlowFunc (("Network connection GUID string = {%ls}\n", pCfgGuidString + 1));
2527
2528 aGUID = Guid (Utf8Str (pCfgGuidString + 1));
2529 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2530 Assert (!aGUID.isEmpty());
2531 }
2532
2533 LogFlowFunc (("vrc=%Vrc\n", vrc));
2534 LogFlowFuncLeave();
2535 return vrc;
2536}
2537
2538/* static */
2539int Host::removeNetworkInterface (SVCHlpClient *aClient,
2540 const Guid &aGUID,
2541 Utf8Str &aErrMsg)
2542{
2543 LogFlowFuncEnter();
2544 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2545
2546 AssertReturn (aClient, VERR_INVALID_POINTER);
2547 AssertReturn (!aGUID.isEmpty(), VERR_INVALID_PARAMETER);
2548
2549 int vrc = VINF_SUCCESS;
2550
2551 do
2552 {
2553 TCHAR lszPnPInstanceId [512] = {0};
2554
2555 /* We have to find the device instance ID through a registry search */
2556
2557 HKEY hkeyNetwork = 0;
2558 HKEY hkeyConnection = 0;
2559
2560 do
2561 {
2562 char strRegLocation [256];
2563 sprintf (strRegLocation,
2564 "SYSTEM\\CurrentControlSet\\Control\\Network\\"
2565 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\{%s}",
2566 aGUID.toString().raw());
2567 LONG status;
2568 status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, strRegLocation, 0,
2569 KEY_READ, &hkeyNetwork);
2570 if ((status != ERROR_SUCCESS) || !hkeyNetwork)
2571 SetErrBreak ((
2572 tr ("Host interface network is not found in registry (%s) [1]"),
2573 strRegLocation));
2574
2575 status = RegOpenKeyExA (hkeyNetwork, "Connection", 0,
2576 KEY_READ, &hkeyConnection);
2577 if ((status != ERROR_SUCCESS) || !hkeyConnection)
2578 SetErrBreak ((
2579 tr ("Host interface network is not found in registry (%s) [2]"),
2580 strRegLocation));
2581
2582 DWORD len = sizeof (lszPnPInstanceId);
2583 DWORD dwKeyType;
2584 status = RegQueryValueExW (hkeyConnection, L"PnPInstanceID", NULL,
2585 &dwKeyType, (LPBYTE) lszPnPInstanceId, &len);
2586 if ((status != ERROR_SUCCESS) || (dwKeyType != REG_SZ))
2587 SetErrBreak ((
2588 tr ("Host interface network is not found in registry (%s) [3]"),
2589 strRegLocation));
2590 }
2591 while (0);
2592
2593 if (hkeyConnection)
2594 RegCloseKey (hkeyConnection);
2595 if (hkeyNetwork)
2596 RegCloseKey (hkeyNetwork);
2597
2598 if (VBOX_FAILURE (vrc))
2599 break;
2600
2601 /*
2602 * Now we are going to enumerate all network devices and
2603 * wait until we encounter the right device instance ID
2604 */
2605
2606 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2607
2608 do
2609 {
2610 BOOL ok;
2611 DWORD ret = 0;
2612 GUID netGuid;
2613 SP_DEVINFO_DATA DeviceInfoData;
2614 DWORD index = 0;
2615 BOOL found = FALSE;
2616 DWORD size = 0;
2617
2618 /* initialize the structure size */
2619 DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
2620
2621 /* copy the net class GUID */
2622 memcpy (&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2623
2624 /* return a device info set contains all installed devices of the Net class */
2625 hDeviceInfo = SetupDiGetClassDevs (&netGuid, NULL, NULL, DIGCF_PRESENT);
2626
2627 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2628 SetErrBreak (("SetupDiGetClassDevs failed (0x%08X)", GetLastError()));
2629
2630 /* enumerate the driver info list */
2631 while (TRUE)
2632 {
2633 TCHAR *deviceHwid;
2634
2635 ok = SetupDiEnumDeviceInfo (hDeviceInfo, index, &DeviceInfoData);
2636
2637 if (!ok)
2638 {
2639 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2640 break;
2641 else
2642 {
2643 index++;
2644 continue;
2645 }
2646 }
2647
2648 /* try to get the hardware ID registry property */
2649 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2650 &DeviceInfoData,
2651 SPDRP_HARDWAREID,
2652 NULL,
2653 NULL,
2654 0,
2655 &size);
2656 if (!ok)
2657 {
2658 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2659 {
2660 index++;
2661 continue;
2662 }
2663
2664 deviceHwid = (TCHAR *) malloc (size);
2665 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2666 &DeviceInfoData,
2667 SPDRP_HARDWAREID,
2668 NULL,
2669 (PBYTE)deviceHwid,
2670 size,
2671 NULL);
2672 if (!ok)
2673 {
2674 free (deviceHwid);
2675 deviceHwid = NULL;
2676 index++;
2677 continue;
2678 }
2679 }
2680 else
2681 {
2682 /* something is wrong. This shouldn't have worked with a NULL buffer */
2683 index++;
2684 continue;
2685 }
2686
2687 for (TCHAR *t = deviceHwid;
2688 t && *t && t < &deviceHwid[size / sizeof(TCHAR)];
2689 t += _tcslen (t) + 1)
2690 {
2691 if (!_tcsicmp (DRIVERHWID, t))
2692 {
2693 /* get the device instance ID */
2694 TCHAR devID [MAX_DEVICE_ID_LEN];
2695 if (CM_Get_Device_ID(DeviceInfoData.DevInst,
2696 devID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2697 {
2698 /* compare to what we determined before */
2699 if (wcscmp(devID, lszPnPInstanceId) == 0)
2700 {
2701 found = TRUE;
2702 break;
2703 }
2704 }
2705 }
2706 }
2707
2708 if (deviceHwid)
2709 {
2710 free (deviceHwid);
2711 deviceHwid = NULL;
2712 }
2713
2714 if (found)
2715 break;
2716
2717 index++;
2718 }
2719
2720 if (found == FALSE)
2721 SetErrBreak ((tr ("Host Interface Network driver not found (0x%08X)"),
2722 GetLastError()));
2723
2724 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2725 if (!ok)
2726 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2727 GetLastError()));
2728
2729 ok = SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2730 if (!ok)
2731 SetErrBreak (("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)",
2732 GetLastError()));
2733 }
2734 while (0);
2735
2736 /* clean up the device info set */
2737 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2738 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2739
2740 if (VBOX_FAILURE (vrc))
2741 break;
2742 }
2743 while (0);
2744
2745 LogFlowFunc (("vrc=%Vrc\n", vrc));
2746 LogFlowFuncLeave();
2747 return vrc;
2748}
2749
2750#undef SetErrBreak
2751
2752/* static */
2753HRESULT Host::networkInterfaceHelperClient (SVCHlpClient *aClient,
2754 Progress *aProgress,
2755 void *aUser, int *aVrc)
2756{
2757 LogFlowFuncEnter();
2758 LogFlowFunc (("aClient={%p}, aProgress={%p}, aUser={%p}\n",
2759 aClient, aProgress, aUser));
2760
2761 AssertReturn ((aClient == NULL && aProgress == NULL && aVrc == NULL) ||
2762 (aClient != NULL && aProgress != NULL && aVrc != NULL),
2763 E_POINTER);
2764 AssertReturn (aUser, E_POINTER);
2765
2766 std::auto_ptr <NetworkInterfaceHelperClientData>
2767 d (static_cast <NetworkInterfaceHelperClientData *> (aUser));
2768
2769 if (aClient == NULL)
2770 {
2771 /* "cleanup only" mode, just return (it will free aUser) */
2772 return S_OK;
2773 }
2774
2775 HRESULT rc = S_OK;
2776 int vrc = VINF_SUCCESS;
2777
2778 switch (d->msgCode)
2779 {
2780 case SVCHlpMsg::CreateHostNetworkInterface:
2781 {
2782 LogFlowFunc (("CreateHostNetworkInterface:\n"));
2783 LogFlowFunc (("Network connection name = '%ls'\n", d->name.raw()));
2784
2785 /* write message and parameters */
2786 vrc = aClient->write (d->msgCode);
2787 if (VBOX_FAILURE (vrc)) break;
2788 vrc = aClient->write (Utf8Str (d->name));
2789 if (VBOX_FAILURE (vrc)) break;
2790
2791 /* wait for a reply */
2792 bool endLoop = false;
2793 while (!endLoop)
2794 {
2795 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
2796
2797 vrc = aClient->read (reply);
2798 if (VBOX_FAILURE (vrc)) break;
2799
2800 switch (reply)
2801 {
2802 case SVCHlpMsg::CreateHostNetworkInterface_OK:
2803 {
2804 /* read the GUID */
2805 Guid guid;
2806 vrc = aClient->read (guid);
2807 if (VBOX_FAILURE (vrc)) break;
2808
2809 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", guid.raw()));
2810
2811 /* initialize the object returned to the caller by
2812 * CreateHostNetworkInterface() */
2813 rc = d->iface->init (d->name, guid);
2814 endLoop = true;
2815 break;
2816 }
2817 case SVCHlpMsg::Error:
2818 {
2819 /* read the error message */
2820 Utf8Str errMsg;
2821 vrc = aClient->read (errMsg);
2822 if (VBOX_FAILURE (vrc)) break;
2823
2824 rc = setError (E_FAIL, errMsg);
2825 endLoop = true;
2826 break;
2827 }
2828 default:
2829 {
2830 endLoop = true;
2831 ComAssertMsgFailedBreak ((
2832 "Invalid message code %d (%08lX)\n",
2833 reply, reply),
2834 rc = E_FAIL);
2835 }
2836 }
2837 }
2838
2839 break;
2840 }
2841 case SVCHlpMsg::RemoveHostNetworkInterface:
2842 {
2843 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
2844 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", d->guid.raw()));
2845
2846 /* write message and parameters */
2847 vrc = aClient->write (d->msgCode);
2848 if (VBOX_FAILURE (vrc)) break;
2849 vrc = aClient->write (d->guid);
2850 if (VBOX_FAILURE (vrc)) break;
2851
2852 /* wait for a reply */
2853 bool endLoop = false;
2854 while (!endLoop)
2855 {
2856 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
2857
2858 vrc = aClient->read (reply);
2859 if (VBOX_FAILURE (vrc)) break;
2860
2861 switch (reply)
2862 {
2863 case SVCHlpMsg::OK:
2864 {
2865 /* no parameters */
2866 rc = S_OK;
2867 endLoop = true;
2868 break;
2869 }
2870 case SVCHlpMsg::Error:
2871 {
2872 /* read the error message */
2873 Utf8Str errMsg;
2874 vrc = aClient->read (errMsg);
2875 if (VBOX_FAILURE (vrc)) break;
2876
2877 rc = setError (E_FAIL, errMsg);
2878 endLoop = true;
2879 break;
2880 }
2881 default:
2882 {
2883 endLoop = true;
2884 ComAssertMsgFailedBreak ((
2885 "Invalid message code %d (%08lX)\n",
2886 reply, reply),
2887 rc = E_FAIL);
2888 }
2889 }
2890 }
2891
2892 break;
2893 }
2894 default:
2895 ComAssertMsgFailedBreak ((
2896 "Invalid message code %d (%08lX)\n",
2897 d->msgCode, d->msgCode),
2898 rc = E_FAIL);
2899 }
2900
2901 if (aVrc)
2902 *aVrc = vrc;
2903
2904 LogFlowFunc (("rc=0x%08X, vrc=%Vrc\n", rc, vrc));
2905 LogFlowFuncLeave();
2906 return rc;
2907}
2908
2909/* static */
2910int Host::networkInterfaceHelperServer (SVCHlpClient *aClient,
2911 SVCHlpMsg::Code aMsgCode)
2912{
2913 LogFlowFuncEnter();
2914 LogFlowFunc (("aClient={%p}, aMsgCode=%d\n", aClient, aMsgCode));
2915
2916 AssertReturn (aClient, VERR_INVALID_POINTER);
2917
2918 int vrc = VINF_SUCCESS;
2919
2920 switch (aMsgCode)
2921 {
2922 case SVCHlpMsg::CreateHostNetworkInterface:
2923 {
2924 LogFlowFunc (("CreateHostNetworkInterface:\n"));
2925
2926 Utf8Str name;
2927 vrc = aClient->read (name);
2928 if (VBOX_FAILURE (vrc)) break;
2929
2930 Guid guid;
2931 Utf8Str errMsg;
2932 vrc = createNetworkInterface (aClient, name, guid, errMsg);
2933
2934 if (VBOX_SUCCESS (vrc))
2935 {
2936 /* write success followed by GUID */
2937 vrc = aClient->write (SVCHlpMsg::CreateHostNetworkInterface_OK);
2938 if (VBOX_FAILURE (vrc)) break;
2939 vrc = aClient->write (guid);
2940 if (VBOX_FAILURE (vrc)) break;
2941 }
2942 else
2943 {
2944 /* write failure followed by error message */
2945 if (errMsg.isEmpty())
2946 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
2947 vrc = aClient->write (SVCHlpMsg::Error);
2948 if (VBOX_FAILURE (vrc)) break;
2949 vrc = aClient->write (errMsg);
2950 if (VBOX_FAILURE (vrc)) break;
2951 }
2952
2953 break;
2954 }
2955 case SVCHlpMsg::RemoveHostNetworkInterface:
2956 {
2957 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
2958
2959 Guid guid;
2960 vrc = aClient->read (guid);
2961 if (VBOX_FAILURE (vrc)) break;
2962
2963 Utf8Str errMsg;
2964 vrc = removeNetworkInterface (aClient, guid, errMsg);
2965
2966 if (VBOX_SUCCESS (vrc))
2967 {
2968 /* write parameter-less success */
2969 vrc = aClient->write (SVCHlpMsg::OK);
2970 if (VBOX_FAILURE (vrc)) break;
2971 }
2972 else
2973 {
2974 /* write failure followed by error message */
2975 if (errMsg.isEmpty())
2976 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
2977 vrc = aClient->write (SVCHlpMsg::Error);
2978 if (VBOX_FAILURE (vrc)) break;
2979 vrc = aClient->write (errMsg);
2980 if (VBOX_FAILURE (vrc)) break;
2981 }
2982
2983 break;
2984 }
2985 default:
2986 AssertMsgFailedBreak ((
2987 "Invalid message code %d (%08lX)\n", aMsgCode, aMsgCode),
2988 VERR_GENERAL_FAILURE);
2989 }
2990
2991 LogFlowFunc (("vrc=%Vrc\n", vrc));
2992 LogFlowFuncLeave();
2993 return vrc;
2994}
2995
2996#endif /* __WIN__ */
2997
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