VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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