VirtualBox

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

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

getenv => RTEnvGet

  • 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#include "HostImpl.h"
54#include "HostDVDDriveImpl.h"
55#include "HostFloppyDriveImpl.h"
56#include "HostUSBDeviceImpl.h"
57#include "USBControllerImpl.h"
58#include "USBDeviceFilterImpl.h"
59#include "USBProxyService.h"
60#include "VirtualBoxImpl.h"
61#include "MachineImpl.h"
62#include "Logging.h"
63
64#ifdef RT_OS_DARWIN
65#include "darwin/iokit.h"
66#endif
67
68#ifdef RT_OS_WINDOWS
69#include "HostNetworkInterfaceImpl.h"
70#endif
71
72#include <VBox/usb.h>
73#include <VBox/err.h>
74#include <iprt/string.h>
75#include <iprt/system.h>
76#include <iprt/time.h>
77#include <iprt/param.h>
78#include <iprt/env.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 (RTEnvGet("VBOX_CDROM"))
211 {
212 char *cdromEnv = strdupa(RTEnvGet("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 (RTEnvGet("VBOX_FLOPPY"))
317 {
318 char *floppyEnv = strdupa(RTEnvGet("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 *tmp = '\0';
1850 }
1851 }
1852 tmp = strstr(mntent->mnt_opts, "dev=");
1853 if (tmp)
1854 {
1855 free(mnt_dev);
1856 mnt_dev = strdup(tmp + strlen("dev="));
1857 if (mnt_dev)
1858 {
1859 tmp = strchr(mnt_dev, ',');
1860 if (tmp)
1861 *tmp = '\0';
1862 }
1863 }
1864 }
1865 // use strstr here to cover things fs types like "udf,iso9660"
1866 if (strstr(mnt_type, "iso9660") == 0)
1867 {
1868 /** @todo check whether we've already got the drive in our list! */
1869 if (validateDevice(mnt_dev, true))
1870 {
1871 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1872 hostDVDDriveObj.createObject();
1873 hostDVDDriveObj->init (Bstr (mnt_dev));
1874 list.push_back (hostDVDDriveObj);
1875 }
1876 }
1877 free(mnt_dev);
1878 free(mnt_type);
1879 }
1880 endmntent(mtab);
1881 }
1882}
1883
1884/**
1885 * Helper function to check whether the given device node is a valid drive
1886 */
1887bool Host::validateDevice(const char *deviceNode, bool isCDROM)
1888{
1889 struct stat statInfo;
1890 bool retValue = false;
1891
1892 // sanity check
1893 if (!deviceNode)
1894 {
1895 return false;
1896 }
1897
1898 // first a simple stat() call
1899 if (stat(deviceNode, &statInfo) < 0)
1900 {
1901 return false;
1902 } else
1903 {
1904 if (isCDROM)
1905 {
1906 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
1907 {
1908 int fileHandle;
1909 // now try to open the device
1910 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
1911 if (fileHandle >= 0)
1912 {
1913 cdrom_subchnl cdChannelInfo;
1914 cdChannelInfo.cdsc_format = CDROM_MSF;
1915 // this call will finally reveal the whole truth
1916 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
1917 (errno == EIO) || (errno == ENOENT) ||
1918 (errno == EINVAL) || (errno == ENOMEDIUM))
1919 {
1920 retValue = true;
1921 }
1922 close(fileHandle);
1923 }
1924 }
1925 } else
1926 {
1927 // floppy case
1928 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
1929 {
1930 /// @todo do some more testing, maybe a nice IOCTL!
1931 retValue = true;
1932 }
1933 }
1934 }
1935 return retValue;
1936}
1937#endif // RT_OS_LINUX
1938
1939/**
1940 * Applies all (golbal and VM) filters to the given USB device. The device
1941 * must be either a newly attached device or a device released by a VM.
1942 *
1943 * This method will request the USB proxy service to release the device (give
1944 * it back to the host) if none of the global or VM filters want to capture
1945 * the device.
1946 *
1947 * @param aDevice USB device to apply filters to.
1948 * @param aMachine Machine the device was released by or @c NULL.
1949 *
1950 * @note the method must be called from under this object's write lock and
1951 * from the aDevice's write lock.
1952 */
1953HRESULT Host::applyAllUSBFilters (ComObjPtr <HostUSBDevice> &aDevice,
1954 SessionMachine *aMachine /* = NULL */)
1955{
1956 LogFlowThisFunc (("\n"));
1957
1958 /// @todo must check for read lock, it's enough here
1959 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
1960
1961 AssertReturn (aDevice->isLockedOnCurrentThread(), E_FAIL);
1962
1963 AssertReturn (aDevice->state() != USBDeviceState_USBDeviceCaptured, E_FAIL);
1964
1965 AssertReturn (aDevice->isStatePending() == false, E_FAIL);
1966
1967 /* ignore unsupported devices */
1968 if (aDevice->state() == USBDeviceState_USBDeviceNotSupported)
1969 return S_OK;
1970 /* ignore unavailable devices as well */
1971 if (aDevice->state() == USBDeviceState_USBDeviceUnavailable)
1972 return S_OK;
1973
1974 VirtualBox::SessionMachineVector machines;
1975 mParent->getOpenedMachines (machines);
1976
1977 /// @todo it may be better to take a copy of filters to iterate and leave
1978 /// the host lock before calling HostUSBDevice:requestCapture() (which
1979 /// calls the VM process).
1980
1981 /* apply global filters */
1982 USBDeviceFilterList::const_iterator it = mUSBDeviceFilters.begin();
1983 for (; it != mUSBDeviceFilters.end(); ++ it)
1984 {
1985 AutoLock filterLock (*it);
1986 const HostUSBDeviceFilter::Data &data = (*it)->data();
1987 if (aDevice->isMatch (data))
1988 {
1989#ifndef VBOX_WITH_USBFILTER
1990 USBDeviceFilterAction_T action = data.mAction;
1991#else
1992 ULONG action = USBDeviceFilterAction_InvalidUSBDeviceFilterAction;
1993 (*it)->COMGETTER (Action) (&action);
1994#endif
1995 if (action == USBDeviceFilterAction_USBDeviceFilterIgnore)
1996 {
1997 /* request to give the device back to the host*/
1998 aDevice->requestRelease();
1999 /* nothing to do any more */
2000 return S_OK;
2001 }
2002 if (action == USBDeviceFilterAction_USBDeviceFilterHold)
2003 break;
2004 }
2005 }
2006
2007 /* apply machine filters */
2008 size_t i = 0;
2009 for (; i < machines.size(); ++ i)
2010 {
2011 /* skip the machine the device was just detached from */
2012 if (aMachine && machines [i] == aMachine)
2013 continue;
2014
2015 if (applyMachineUSBFilters (machines [i], aDevice))
2016 break;
2017 }
2018
2019 if (i == machines.size())
2020 {
2021 /* no matched machine filters, check what to do */
2022 if (it == mUSBDeviceFilters.end())
2023 {
2024 /* no any filter matched at all */
2025 /* request to give the device back to the host */
2026 aDevice->requestRelease();
2027 }
2028 else
2029 {
2030 /* there was a global Hold filter */
2031 aDevice->requestHold();
2032 }
2033 }
2034
2035 return S_OK;
2036}
2037
2038/**
2039 * Runs through filters of the given machine and asks the USB proxy service
2040 * to capture the given USB device when there is a match.
2041 *
2042 * @param aMachine Machine whose filters are to be run.
2043 * @param aDevice USB device, a candidate for auto-capturing.
2044 * @return @c true if there was a match and @c false otherwise.
2045 *
2046 * @note the method must be called from under this object's write lock and
2047 * from the aDevice's write lock.
2048 *
2049 * @note Locks aMachine for reading.
2050 */
2051bool Host::applyMachineUSBFilters (SessionMachine *aMachine,
2052 ComObjPtr <HostUSBDevice> &aDevice)
2053{
2054 LogFlowThisFunc (("\n"));
2055
2056 AssertReturn (aMachine, false);
2057
2058 /// @todo must check for read lock, it's enough here
2059 AssertReturn (isLockedOnCurrentThread(), false);
2060
2061 AssertReturn (aDevice->isLockedOnCurrentThread(), false);
2062
2063 AssertReturn (aDevice->state() != USBDeviceState_USBDeviceNotSupported, false);
2064 AssertReturn (aDevice->state() != USBDeviceState_USBDeviceUnavailable, false);
2065
2066 AssertReturn (aDevice->isStatePending() == false, false);
2067
2068 bool hasMatch = false;
2069
2070 {
2071 /* We're going to use aMachine which is not our child/parent, add a
2072 * caller */
2073 AutoCaller autoCaller (aMachine);
2074 if (!autoCaller.isOk())
2075 {
2076 /* silently return, the machine might be not running any more */
2077 return false;
2078 }
2079
2080 /* enter the machine's lock because we want to access its USB controller */
2081 AutoReaderLock machineLock (aMachine);
2082 hasMatch = aMachine->usbController()->hasMatchingFilter (aDevice);
2083 }
2084
2085 if (hasMatch)
2086 {
2087 /* try to capture the device */
2088 return aDevice->requestCapture (aMachine);
2089 }
2090
2091 return hasMatch;
2092}
2093
2094/**
2095 * Called by USB proxy service when a new device is physically attached
2096 * to the host.
2097 *
2098 * @param aDevice Pointer to the device which has been attached.
2099 */
2100void Host::onUSBDeviceAttached (HostUSBDevice *aDevice)
2101{
2102 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2103
2104 AssertReturnVoid (aDevice);
2105
2106 AssertReturnVoid (isLockedOnCurrentThread());
2107 AssertReturnVoid (aDevice->isLockedOnCurrentThread());
2108
2109 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2110 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2111 aDevice->pendingState()));
2112
2113 Assert (aDevice->isStatePending() == false);
2114
2115 /* add to the collecion */
2116 mUSBDevices.push_back (aDevice);
2117
2118 /* apply all filters */
2119 ComObjPtr <HostUSBDevice> device (aDevice);
2120 HRESULT rc = applyAllUSBFilters (device);
2121 AssertComRC (rc);
2122}
2123
2124/**
2125 * Called by USB proxy service when the device is physically detached
2126 * from the host.
2127 *
2128 * @param aDevice Pointer to the device which has been detached.
2129 */
2130void Host::onUSBDeviceDetached (HostUSBDevice *aDevice)
2131{
2132 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2133
2134 AssertReturnVoid (aDevice);
2135
2136 AssertReturnVoid (isLockedOnCurrentThread());
2137 AssertReturnVoid (aDevice->isLockedOnCurrentThread());
2138
2139 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2140 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2141 aDevice->pendingState()));
2142
2143 Guid id = aDevice->id();
2144
2145 ComObjPtr <HostUSBDevice> device;
2146 Host::USBDeviceList::iterator it = mUSBDevices.begin();
2147 while (it != mUSBDevices.end())
2148 {
2149 if ((*it)->id() == id)
2150 {
2151 device = (*it);
2152 break;
2153 }
2154 ++ it;
2155 }
2156
2157 AssertReturnVoid (!!device);
2158
2159 /* remove from the collecion */
2160 mUSBDevices.erase (it);
2161
2162 /* Detach the device from any machine currently using it,
2163 reset all data and uninitialize the device object. */
2164 device->onDetachedPhys();
2165}
2166
2167/**
2168 * Called by USB proxy service when the state of the device has changed
2169 * either because of the state change request or because of some external
2170 * interaction.
2171 *
2172 * @param aDevice The device in question.
2173 */
2174void Host::onUSBDeviceStateChanged (HostUSBDevice *aDevice)
2175{
2176 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2177
2178 AssertReturnVoid (aDevice);
2179
2180 AssertReturnVoid (isLockedOnCurrentThread());
2181 AssertReturnVoid (aDevice->isLockedOnCurrentThread());
2182
2183 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2184 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2185 aDevice->pendingState()));
2186
2187
2188 ComObjPtr <HostUSBDevice> device (aDevice);
2189 if (device->isStatePending())
2190 {
2191 /* it was a state change request */
2192 if (device->pendingStateEx() == HostUSBDevice::kDetachingPendingAttachFilters)
2193 {
2194 /* The device has completed an asynchronous detach operation, subject
2195 it to the filters and such if the current state permits this.
2196 (handlePendingStateChange will disassociate itself from the machine.) */
2197 ComObjPtr <SessionMachine> machine (device->machine());
2198 device->handlePendingStateChange();
2199 if (device->state() == USBDeviceState_USBDeviceCaptured)
2200 {
2201 Log (("USB: running filters on async detached device\n"));
2202 device->setHeld();
2203 HRESULT rc = applyAllUSBFilters (device, machine);
2204 AssertComRC (rc);
2205 }
2206 else
2207 Log (("USB: async detached devices reappeared in stated %d instead of %d!\n",
2208 device->state(), USBDeviceState_USBDeviceCaptured));
2209 }
2210 else
2211 device->handlePendingStateChange();
2212 }
2213 else if ( device->state() == USBDeviceState_USBDeviceAvailable
2214 || device->state() == USBDeviceState_USBDeviceBusy)
2215 {
2216 /* The device has gone from being unavailable (not subject to filters) to being
2217 available / busy. This transition can be triggered by udevd or manual
2218 permission changes on Linux. On all systems may be triggered by the host
2219 ceasing to use the device - like unmounting an MSD in the Finder or invoking
2220 the "Safely remove XXXX" stuff on Windows (perhaps). */
2221 HRESULT rc = applyAllUSBFilters (device);
2222 AssertComRC (rc);
2223 }
2224 else
2225 {
2226 /* some external state change */
2227
2228 /// @todo re-run all USB filters probably
2229 AssertFailed();
2230 }
2231}
2232
2233/**
2234 * Checks for the presense and status of the USB Proxy Service.
2235 * Returns S_OK when the Proxy is present and OK, or E_FAIL and a
2236 * corresponding error message otherwise. Intended to be used by methods
2237 * that rely on the Proxy Service availability.
2238 *
2239 * @note Locks this object for reading.
2240 */
2241HRESULT Host::checkUSBProxyService()
2242{
2243#ifdef VBOX_WITH_USB
2244 AutoLock lock (this);
2245 CHECK_READY();
2246
2247 AssertReturn (mUSBProxyService, E_FAIL);
2248 if (!mUSBProxyService->isActive())
2249 {
2250 /* disable the USB controller completely to avoid assertions if the
2251 * USB proxy service could not start. */
2252
2253 Assert (VBOX_FAILURE (mUSBProxyService->getLastError()));
2254 if (mUSBProxyService->getLastError() == VERR_FILE_NOT_FOUND)
2255 return setError (E_FAIL,
2256 tr ("Could not load the Host USB Proxy Service (%Vrc). "
2257 "The service might be not installed on the host computer"),
2258 mUSBProxyService->getLastError());
2259 else
2260 return setError (E_FAIL,
2261 tr ("Could not load the Host USB Proxy service (%Vrc)"),
2262 mUSBProxyService->getLastError());
2263 }
2264
2265 return S_OK;
2266#else
2267 return E_NOTIMPL;
2268#endif
2269}
2270
2271#ifdef RT_OS_WINDOWS
2272
2273/* The original source of the VBoxTAP adapter creation/destruction code has the following copyright */
2274/*
2275 Copyright 2004 by the Massachusetts Institute of Technology
2276
2277 All rights reserved.
2278
2279 Permission to use, copy, modify, and distribute this software and its
2280 documentation for any purpose and without fee is hereby granted,
2281 provided that the above copyright notice appear in all copies and that
2282 both that copyright notice and this permission notice appear in
2283 supporting documentation, and that the name of the Massachusetts
2284 Institute of Technology (M.I.T.) not be used in advertising or publicity
2285 pertaining to distribution of the software without specific, written
2286 prior permission.
2287
2288 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
2289 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
2290 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
2291 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
2292 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2293 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2294 SOFTWARE.
2295*/
2296
2297
2298#define NETSHELL_LIBRARY _T("netshell.dll")
2299
2300/**
2301 * Use the IShellFolder API to rename the connection.
2302 */
2303static HRESULT rename_shellfolder (PCWSTR wGuid, PCWSTR wNewName)
2304{
2305 /* This is the GUID for the network connections folder. It is constant.
2306 * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */
2307 const GUID CLSID_NetworkConnections = {
2308 0x7007ACC7, 0x3202, 0x11D1, {
2309 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
2310 }
2311 };
2312
2313 LPITEMIDLIST pidl = NULL;
2314 IShellFolder *pShellFolder = NULL;
2315 HRESULT hr;
2316
2317 /* Build the display name in the form "::{GUID}". */
2318 if (wcslen (wGuid) >= MAX_PATH)
2319 return E_INVALIDARG;
2320 WCHAR szAdapterGuid[MAX_PATH + 2] = {0};
2321 swprintf (szAdapterGuid, L"::%ls", wGuid);
2322
2323 /* Create an instance of the network connections folder. */
2324 hr = CoCreateInstance (CLSID_NetworkConnections, NULL,
2325 CLSCTX_INPROC_SERVER, IID_IShellFolder,
2326 reinterpret_cast <LPVOID *> (&pShellFolder));
2327 /* Parse the display name. */
2328 if (SUCCEEDED (hr))
2329 {
2330 hr = pShellFolder->ParseDisplayName (NULL, NULL, szAdapterGuid, NULL,
2331 &pidl, NULL);
2332 }
2333 if (SUCCEEDED (hr))
2334 {
2335 hr = pShellFolder->SetNameOf (NULL, pidl, wNewName, SHGDN_NORMAL,
2336 &pidl);
2337 }
2338
2339 CoTaskMemFree (pidl);
2340
2341 if (pShellFolder)
2342 pShellFolder->Release();
2343
2344 return hr;
2345}
2346
2347extern "C" HRESULT RenameConnection (PCWSTR GuidString, PCWSTR NewName)
2348{
2349 typedef HRESULT (WINAPI *lpHrRenameConnection) (const GUID *, PCWSTR);
2350 lpHrRenameConnection RenameConnectionFunc = NULL;
2351 HRESULT status;
2352
2353 /* First try the IShellFolder interface, which was unimplemented
2354 * for the network connections folder before XP. */
2355 status = rename_shellfolder (GuidString, NewName);
2356 if (status == E_NOTIMPL)
2357 {
2358/** @todo that code doesn't seem to work! */
2359 /* The IShellFolder interface is not implemented on this platform.
2360 * Try the (undocumented) HrRenameConnection API in the netshell
2361 * library. */
2362 CLSID clsid;
2363 HINSTANCE hNetShell;
2364 status = CLSIDFromString ((LPOLESTR) GuidString, &clsid);
2365 if (FAILED(status))
2366 return E_FAIL;
2367 hNetShell = LoadLibrary (NETSHELL_LIBRARY);
2368 if (hNetShell == NULL)
2369 return E_FAIL;
2370 RenameConnectionFunc =
2371 (lpHrRenameConnection) GetProcAddress (hNetShell,
2372 "HrRenameConnection");
2373 if (RenameConnectionFunc == NULL)
2374 {
2375 FreeLibrary (hNetShell);
2376 return E_FAIL;
2377 }
2378 status = RenameConnectionFunc (&clsid, NewName);
2379 FreeLibrary (hNetShell);
2380 }
2381 if (FAILED (status))
2382 return status;
2383
2384 return S_OK;
2385}
2386
2387#define DRIVERHWID _T("vboxtap")
2388
2389#define SetErrBreak(strAndArgs) \
2390 if (1) { \
2391 aErrMsg = Utf8StrFmt strAndArgs; vrc = VERR_GENERAL_FAILURE; break; \
2392 } else do {} while (0)
2393
2394/* static */
2395int Host::createNetworkInterface (SVCHlpClient *aClient,
2396 const Utf8Str &aName,
2397 Guid &aGUID, Utf8Str &aErrMsg)
2398{
2399 LogFlowFuncEnter();
2400 LogFlowFunc (("Network connection name = '%s'\n", aName.raw()));
2401
2402 AssertReturn (aClient, VERR_INVALID_POINTER);
2403 AssertReturn (!aName.isNull(), VERR_INVALID_PARAMETER);
2404
2405 int vrc = VINF_SUCCESS;
2406
2407 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2408 SP_DEVINFO_DATA DeviceInfoData;
2409 DWORD ret = 0;
2410 BOOL found = FALSE;
2411 BOOL registered = FALSE;
2412 BOOL destroyList = FALSE;
2413 TCHAR pCfgGuidString [50];
2414
2415 do
2416 {
2417 BOOL ok;
2418 GUID netGuid;
2419 SP_DRVINFO_DATA DriverInfoData;
2420 SP_DEVINSTALL_PARAMS DeviceInstallParams;
2421 TCHAR className [MAX_PATH];
2422 DWORD index = 0;
2423 PSP_DRVINFO_DETAIL_DATA pDriverInfoDetail;
2424 /* for our purposes, 2k buffer is more
2425 * than enough to obtain the hardware ID
2426 * of the VBoxTAP driver. */
2427 DWORD detailBuf [2048];
2428
2429 HKEY hkey = NULL;
2430 DWORD cbSize;
2431 DWORD dwValueType;
2432
2433 /* initialize the structure size */
2434 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
2435 DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
2436
2437 /* copy the net class GUID */
2438 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
2439
2440 /* create an empty device info set associated with the net class GUID */
2441 hDeviceInfo = SetupDiCreateDeviceInfoList (&netGuid, NULL);
2442 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2443 SetErrBreak (("SetupDiCreateDeviceInfoList failed (0x%08X)",
2444 GetLastError()));
2445
2446 /* get the class name from GUID */
2447 ok = SetupDiClassNameFromGuid (&netGuid, className, MAX_PATH, NULL);
2448 if (!ok)
2449 SetErrBreak (("SetupDiClassNameFromGuid failed (0x%08X)",
2450 GetLastError()));
2451
2452 /* create a device info element and add the new device instance
2453 * key to registry */
2454 ok = SetupDiCreateDeviceInfo (hDeviceInfo, className, &netGuid, NULL, NULL,
2455 DICD_GENERATE_ID, &DeviceInfoData);
2456 if (!ok)
2457 SetErrBreak (("SetupDiCreateDeviceInfo failed (0x%08X)",
2458 GetLastError()));
2459
2460 /* select the newly created device info to be the currently
2461 selected member */
2462 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2463 if (!ok)
2464 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2465 GetLastError()));
2466
2467 /* build a list of class drivers */
2468 ok = SetupDiBuildDriverInfoList (hDeviceInfo, &DeviceInfoData,
2469 SPDIT_CLASSDRIVER);
2470 if (!ok)
2471 SetErrBreak (("SetupDiBuildDriverInfoList failed (0x%08X)",
2472 GetLastError()));
2473
2474 destroyList = TRUE;
2475
2476 /* enumerate the driver info list */
2477 while (TRUE)
2478 {
2479 BOOL ret;
2480
2481 ret = SetupDiEnumDriverInfo (hDeviceInfo, &DeviceInfoData,
2482 SPDIT_CLASSDRIVER, index, &DriverInfoData);
2483
2484 /* if the function failed and GetLastError() returned
2485 * ERROR_NO_MORE_ITEMS, then we have reached the end of the
2486 * list. Othewise there was something wrong with this
2487 * particular driver. */
2488 if (!ret)
2489 {
2490 if(GetLastError() == ERROR_NO_MORE_ITEMS)
2491 break;
2492 else
2493 {
2494 index++;
2495 continue;
2496 }
2497 }
2498
2499 pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA) detailBuf;
2500 pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
2501
2502 /* if we successfully find the hardware ID and it turns out to
2503 * be the one for the loopback driver, then we are done. */
2504 if (SetupDiGetDriverInfoDetail (hDeviceInfo,
2505 &DeviceInfoData,
2506 &DriverInfoData,
2507 pDriverInfoDetail,
2508 sizeof (detailBuf),
2509 NULL))
2510 {
2511 TCHAR * t;
2512
2513 /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the
2514 * whole list and see if there is a match somewhere. */
2515 t = pDriverInfoDetail->HardwareID;
2516 while (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2517 {
2518 if (!_tcsicmp(t, DRIVERHWID))
2519 break;
2520
2521 t += _tcslen(t) + 1;
2522 }
2523
2524 if (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2525 {
2526 found = TRUE;
2527 break;
2528 }
2529 }
2530
2531 index ++;
2532 }
2533
2534 if (!found)
2535 SetErrBreak ((tr ("Could not find Host Interface Networking driver! "
2536 "Please reinstall")));
2537
2538 /* set the loopback driver to be the currently selected */
2539 ok = SetupDiSetSelectedDriver (hDeviceInfo, &DeviceInfoData,
2540 &DriverInfoData);
2541 if (!ok)
2542 SetErrBreak (("SetupDiSetSelectedDriver failed (0x%08X)",
2543 GetLastError()));
2544
2545 /* register the phantom device to prepare for install */
2546 ok = SetupDiCallClassInstaller (DIF_REGISTERDEVICE, hDeviceInfo,
2547 &DeviceInfoData);
2548 if (!ok)
2549 SetErrBreak (("SetupDiCallClassInstaller failed (0x%08X)",
2550 GetLastError()));
2551
2552 /* registered, but remove if errors occur in the following code */
2553 registered = TRUE;
2554
2555 /* ask the installer if we can install the device */
2556 ok = SetupDiCallClassInstaller (DIF_ALLOW_INSTALL, hDeviceInfo,
2557 &DeviceInfoData);
2558 if (!ok)
2559 {
2560 if (GetLastError() != ERROR_DI_DO_DEFAULT)
2561 SetErrBreak (("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (0x%08X)",
2562 GetLastError()));
2563 /* that's fine */
2564 }
2565
2566 /* install the files first */
2567 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES, hDeviceInfo,
2568 &DeviceInfoData);
2569 if (!ok)
2570 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (0x%08X)",
2571 GetLastError()));
2572
2573 /* get the device install parameters and disable filecopy */
2574 DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
2575 ok = SetupDiGetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2576 &DeviceInstallParams);
2577 if (ok)
2578 {
2579 DeviceInstallParams.Flags |= DI_NOFILECOPY;
2580 ok = SetupDiSetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2581 &DeviceInstallParams);
2582 if (!ok)
2583 SetErrBreak (("SetupDiSetDeviceInstallParams failed (0x%08X)",
2584 GetLastError()));
2585 }
2586
2587 /*
2588 * Register any device-specific co-installers for this device,
2589 */
2590
2591 ok = SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS,
2592 hDeviceInfo,
2593 &DeviceInfoData);
2594 if (!ok)
2595 SetErrBreak (("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (0x%08X)",
2596 GetLastError()));
2597
2598 /*
2599 * install any installer-specified interfaces.
2600 * and then do the real install
2601 */
2602 ok = SetupDiCallClassInstaller (DIF_INSTALLINTERFACES,
2603 hDeviceInfo,
2604 &DeviceInfoData);
2605 if (!ok)
2606 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (0x%08X)",
2607 GetLastError()));
2608
2609 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICE,
2610 hDeviceInfo,
2611 &DeviceInfoData);
2612 if (!ok)
2613 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (0x%08X)",
2614 GetLastError()));
2615
2616 /* Figure out NetCfgInstanceId */
2617 hkey = SetupDiOpenDevRegKey (hDeviceInfo,
2618 &DeviceInfoData,
2619 DICS_FLAG_GLOBAL,
2620 0,
2621 DIREG_DRV,
2622 KEY_READ);
2623 if (hkey == INVALID_HANDLE_VALUE)
2624 SetErrBreak (("SetupDiOpenDevRegKey failed (0x%08X)",
2625 GetLastError()));
2626
2627 cbSize = sizeof (pCfgGuidString);
2628 DWORD ret;
2629 ret = RegQueryValueEx (hkey, _T ("NetCfgInstanceId"), NULL,
2630 &dwValueType, (LPBYTE) pCfgGuidString, &cbSize);
2631 RegCloseKey (hkey);
2632
2633 ret = RenameConnection (pCfgGuidString, Bstr (aName));
2634 if (FAILED (ret))
2635 SetErrBreak (("Failed to set interface name (ret=0x%08X, "
2636 "pCfgGuidString='%ls', cbSize=%d)",
2637 ret, pCfgGuidString, cbSize));
2638 }
2639 while (0);
2640
2641 /*
2642 * cleanup
2643 */
2644
2645 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2646 {
2647 /* an error has occured, but the device is registered, we must remove it */
2648 if (ret != 0 && registered)
2649 SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2650
2651 found = SetupDiDeleteDeviceInfo (hDeviceInfo, &DeviceInfoData);
2652
2653 /* destroy the driver info list */
2654 if (destroyList)
2655 SetupDiDestroyDriverInfoList (hDeviceInfo, &DeviceInfoData,
2656 SPDIT_CLASSDRIVER);
2657 /* clean up the device info set */
2658 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2659 }
2660
2661 /* return the network connection GUID on success */
2662 if (VBOX_SUCCESS (vrc))
2663 {
2664 /* remove the curly bracket at the end */
2665 pCfgGuidString [_tcslen (pCfgGuidString) - 1] = '\0';
2666 LogFlowFunc (("Network connection GUID string = {%ls}\n", pCfgGuidString + 1));
2667
2668 aGUID = Guid (Utf8Str (pCfgGuidString + 1));
2669 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2670 Assert (!aGUID.isEmpty());
2671 }
2672
2673 LogFlowFunc (("vrc=%Vrc\n", vrc));
2674 LogFlowFuncLeave();
2675 return vrc;
2676}
2677
2678/* static */
2679int Host::removeNetworkInterface (SVCHlpClient *aClient,
2680 const Guid &aGUID,
2681 Utf8Str &aErrMsg)
2682{
2683 LogFlowFuncEnter();
2684 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2685
2686 AssertReturn (aClient, VERR_INVALID_POINTER);
2687 AssertReturn (!aGUID.isEmpty(), VERR_INVALID_PARAMETER);
2688
2689 int vrc = VINF_SUCCESS;
2690
2691 do
2692 {
2693 TCHAR lszPnPInstanceId [512] = {0};
2694
2695 /* We have to find the device instance ID through a registry search */
2696
2697 HKEY hkeyNetwork = 0;
2698 HKEY hkeyConnection = 0;
2699
2700 do
2701 {
2702 char strRegLocation [256];
2703 sprintf (strRegLocation,
2704 "SYSTEM\\CurrentControlSet\\Control\\Network\\"
2705 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\{%s}",
2706 aGUID.toString().raw());
2707 LONG status;
2708 status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, strRegLocation, 0,
2709 KEY_READ, &hkeyNetwork);
2710 if ((status != ERROR_SUCCESS) || !hkeyNetwork)
2711 SetErrBreak ((
2712 tr ("Host interface network is not found in registry (%s) [1]"),
2713 strRegLocation));
2714
2715 status = RegOpenKeyExA (hkeyNetwork, "Connection", 0,
2716 KEY_READ, &hkeyConnection);
2717 if ((status != ERROR_SUCCESS) || !hkeyConnection)
2718 SetErrBreak ((
2719 tr ("Host interface network is not found in registry (%s) [2]"),
2720 strRegLocation));
2721
2722 DWORD len = sizeof (lszPnPInstanceId);
2723 DWORD dwKeyType;
2724 status = RegQueryValueExW (hkeyConnection, L"PnPInstanceID", NULL,
2725 &dwKeyType, (LPBYTE) lszPnPInstanceId, &len);
2726 if ((status != ERROR_SUCCESS) || (dwKeyType != REG_SZ))
2727 SetErrBreak ((
2728 tr ("Host interface network is not found in registry (%s) [3]"),
2729 strRegLocation));
2730 }
2731 while (0);
2732
2733 if (hkeyConnection)
2734 RegCloseKey (hkeyConnection);
2735 if (hkeyNetwork)
2736 RegCloseKey (hkeyNetwork);
2737
2738 if (VBOX_FAILURE (vrc))
2739 break;
2740
2741 /*
2742 * Now we are going to enumerate all network devices and
2743 * wait until we encounter the right device instance ID
2744 */
2745
2746 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2747
2748 do
2749 {
2750 BOOL ok;
2751 DWORD ret = 0;
2752 GUID netGuid;
2753 SP_DEVINFO_DATA DeviceInfoData;
2754 DWORD index = 0;
2755 BOOL found = FALSE;
2756 DWORD size = 0;
2757
2758 /* initialize the structure size */
2759 DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
2760
2761 /* copy the net class GUID */
2762 memcpy (&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2763
2764 /* return a device info set contains all installed devices of the Net class */
2765 hDeviceInfo = SetupDiGetClassDevs (&netGuid, NULL, NULL, DIGCF_PRESENT);
2766
2767 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2768 SetErrBreak (("SetupDiGetClassDevs failed (0x%08X)", GetLastError()));
2769
2770 /* enumerate the driver info list */
2771 while (TRUE)
2772 {
2773 TCHAR *deviceHwid;
2774
2775 ok = SetupDiEnumDeviceInfo (hDeviceInfo, index, &DeviceInfoData);
2776
2777 if (!ok)
2778 {
2779 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2780 break;
2781 else
2782 {
2783 index++;
2784 continue;
2785 }
2786 }
2787
2788 /* try to get the hardware ID registry property */
2789 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2790 &DeviceInfoData,
2791 SPDRP_HARDWAREID,
2792 NULL,
2793 NULL,
2794 0,
2795 &size);
2796 if (!ok)
2797 {
2798 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2799 {
2800 index++;
2801 continue;
2802 }
2803
2804 deviceHwid = (TCHAR *) malloc (size);
2805 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2806 &DeviceInfoData,
2807 SPDRP_HARDWAREID,
2808 NULL,
2809 (PBYTE)deviceHwid,
2810 size,
2811 NULL);
2812 if (!ok)
2813 {
2814 free (deviceHwid);
2815 deviceHwid = NULL;
2816 index++;
2817 continue;
2818 }
2819 }
2820 else
2821 {
2822 /* something is wrong. This shouldn't have worked with a NULL buffer */
2823 index++;
2824 continue;
2825 }
2826
2827 for (TCHAR *t = deviceHwid;
2828 t && *t && t < &deviceHwid[size / sizeof(TCHAR)];
2829 t += _tcslen (t) + 1)
2830 {
2831 if (!_tcsicmp (DRIVERHWID, t))
2832 {
2833 /* get the device instance ID */
2834 TCHAR devID [MAX_DEVICE_ID_LEN];
2835 if (CM_Get_Device_ID(DeviceInfoData.DevInst,
2836 devID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2837 {
2838 /* compare to what we determined before */
2839 if (wcscmp(devID, lszPnPInstanceId) == 0)
2840 {
2841 found = TRUE;
2842 break;
2843 }
2844 }
2845 }
2846 }
2847
2848 if (deviceHwid)
2849 {
2850 free (deviceHwid);
2851 deviceHwid = NULL;
2852 }
2853
2854 if (found)
2855 break;
2856
2857 index++;
2858 }
2859
2860 if (found == FALSE)
2861 SetErrBreak ((tr ("Host Interface Network driver not found (0x%08X)"),
2862 GetLastError()));
2863
2864 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2865 if (!ok)
2866 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2867 GetLastError()));
2868
2869 ok = SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2870 if (!ok)
2871 SetErrBreak (("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)",
2872 GetLastError()));
2873 }
2874 while (0);
2875
2876 /* clean up the device info set */
2877 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2878 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2879
2880 if (VBOX_FAILURE (vrc))
2881 break;
2882 }
2883 while (0);
2884
2885 LogFlowFunc (("vrc=%Vrc\n", vrc));
2886 LogFlowFuncLeave();
2887 return vrc;
2888}
2889
2890#undef SetErrBreak
2891
2892/* static */
2893HRESULT Host::networkInterfaceHelperClient (SVCHlpClient *aClient,
2894 Progress *aProgress,
2895 void *aUser, int *aVrc)
2896{
2897 LogFlowFuncEnter();
2898 LogFlowFunc (("aClient={%p}, aProgress={%p}, aUser={%p}\n",
2899 aClient, aProgress, aUser));
2900
2901 AssertReturn ((aClient == NULL && aProgress == NULL && aVrc == NULL) ||
2902 (aClient != NULL && aProgress != NULL && aVrc != NULL),
2903 E_POINTER);
2904 AssertReturn (aUser, E_POINTER);
2905
2906 std::auto_ptr <NetworkInterfaceHelperClientData>
2907 d (static_cast <NetworkInterfaceHelperClientData *> (aUser));
2908
2909 if (aClient == NULL)
2910 {
2911 /* "cleanup only" mode, just return (it will free aUser) */
2912 return S_OK;
2913 }
2914
2915 HRESULT rc = S_OK;
2916 int vrc = VINF_SUCCESS;
2917
2918 switch (d->msgCode)
2919 {
2920 case SVCHlpMsg::CreateHostNetworkInterface:
2921 {
2922 LogFlowFunc (("CreateHostNetworkInterface:\n"));
2923 LogFlowFunc (("Network connection name = '%ls'\n", d->name.raw()));
2924
2925 /* write message and parameters */
2926 vrc = aClient->write (d->msgCode);
2927 if (VBOX_FAILURE (vrc)) break;
2928 vrc = aClient->write (Utf8Str (d->name));
2929 if (VBOX_FAILURE (vrc)) break;
2930
2931 /* wait for a reply */
2932 bool endLoop = false;
2933 while (!endLoop)
2934 {
2935 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
2936
2937 vrc = aClient->read (reply);
2938 if (VBOX_FAILURE (vrc)) break;
2939
2940 switch (reply)
2941 {
2942 case SVCHlpMsg::CreateHostNetworkInterface_OK:
2943 {
2944 /* read the GUID */
2945 Guid guid;
2946 vrc = aClient->read (guid);
2947 if (VBOX_FAILURE (vrc)) break;
2948
2949 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", guid.raw()));
2950
2951 /* initialize the object returned to the caller by
2952 * CreateHostNetworkInterface() */
2953 rc = d->iface->init (d->name, guid);
2954 endLoop = true;
2955 break;
2956 }
2957 case SVCHlpMsg::Error:
2958 {
2959 /* read the error message */
2960 Utf8Str errMsg;
2961 vrc = aClient->read (errMsg);
2962 if (VBOX_FAILURE (vrc)) break;
2963
2964 rc = setError (E_FAIL, errMsg);
2965 endLoop = true;
2966 break;
2967 }
2968 default:
2969 {
2970 endLoop = true;
2971 ComAssertMsgFailedBreak ((
2972 "Invalid message code %d (%08lX)\n",
2973 reply, reply),
2974 rc = E_FAIL);
2975 }
2976 }
2977 }
2978
2979 break;
2980 }
2981 case SVCHlpMsg::RemoveHostNetworkInterface:
2982 {
2983 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
2984 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", d->guid.raw()));
2985
2986 /* write message and parameters */
2987 vrc = aClient->write (d->msgCode);
2988 if (VBOX_FAILURE (vrc)) break;
2989 vrc = aClient->write (d->guid);
2990 if (VBOX_FAILURE (vrc)) break;
2991
2992 /* wait for a reply */
2993 bool endLoop = false;
2994 while (!endLoop)
2995 {
2996 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
2997
2998 vrc = aClient->read (reply);
2999 if (VBOX_FAILURE (vrc)) break;
3000
3001 switch (reply)
3002 {
3003 case SVCHlpMsg::OK:
3004 {
3005 /* no parameters */
3006 rc = S_OK;
3007 endLoop = true;
3008 break;
3009 }
3010 case SVCHlpMsg::Error:
3011 {
3012 /* read the error message */
3013 Utf8Str errMsg;
3014 vrc = aClient->read (errMsg);
3015 if (VBOX_FAILURE (vrc)) break;
3016
3017 rc = setError (E_FAIL, errMsg);
3018 endLoop = true;
3019 break;
3020 }
3021 default:
3022 {
3023 endLoop = true;
3024 ComAssertMsgFailedBreak ((
3025 "Invalid message code %d (%08lX)\n",
3026 reply, reply),
3027 rc = E_FAIL);
3028 }
3029 }
3030 }
3031
3032 break;
3033 }
3034 default:
3035 ComAssertMsgFailedBreak ((
3036 "Invalid message code %d (%08lX)\n",
3037 d->msgCode, d->msgCode),
3038 rc = E_FAIL);
3039 }
3040
3041 if (aVrc)
3042 *aVrc = vrc;
3043
3044 LogFlowFunc (("rc=0x%08X, vrc=%Vrc\n", rc, vrc));
3045 LogFlowFuncLeave();
3046 return rc;
3047}
3048
3049/* static */
3050int Host::networkInterfaceHelperServer (SVCHlpClient *aClient,
3051 SVCHlpMsg::Code aMsgCode)
3052{
3053 LogFlowFuncEnter();
3054 LogFlowFunc (("aClient={%p}, aMsgCode=%d\n", aClient, aMsgCode));
3055
3056 AssertReturn (aClient, VERR_INVALID_POINTER);
3057
3058 int vrc = VINF_SUCCESS;
3059
3060 switch (aMsgCode)
3061 {
3062 case SVCHlpMsg::CreateHostNetworkInterface:
3063 {
3064 LogFlowFunc (("CreateHostNetworkInterface:\n"));
3065
3066 Utf8Str name;
3067 vrc = aClient->read (name);
3068 if (VBOX_FAILURE (vrc)) break;
3069
3070 Guid guid;
3071 Utf8Str errMsg;
3072 vrc = createNetworkInterface (aClient, name, guid, errMsg);
3073
3074 if (VBOX_SUCCESS (vrc))
3075 {
3076 /* write success followed by GUID */
3077 vrc = aClient->write (SVCHlpMsg::CreateHostNetworkInterface_OK);
3078 if (VBOX_FAILURE (vrc)) break;
3079 vrc = aClient->write (guid);
3080 if (VBOX_FAILURE (vrc)) break;
3081 }
3082 else
3083 {
3084 /* write failure followed by error message */
3085 if (errMsg.isEmpty())
3086 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
3087 vrc = aClient->write (SVCHlpMsg::Error);
3088 if (VBOX_FAILURE (vrc)) break;
3089 vrc = aClient->write (errMsg);
3090 if (VBOX_FAILURE (vrc)) break;
3091 }
3092
3093 break;
3094 }
3095 case SVCHlpMsg::RemoveHostNetworkInterface:
3096 {
3097 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
3098
3099 Guid guid;
3100 vrc = aClient->read (guid);
3101 if (VBOX_FAILURE (vrc)) break;
3102
3103 Utf8Str errMsg;
3104 vrc = removeNetworkInterface (aClient, guid, errMsg);
3105
3106 if (VBOX_SUCCESS (vrc))
3107 {
3108 /* write parameter-less success */
3109 vrc = aClient->write (SVCHlpMsg::OK);
3110 if (VBOX_FAILURE (vrc)) break;
3111 }
3112 else
3113 {
3114 /* write failure followed by error message */
3115 if (errMsg.isEmpty())
3116 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
3117 vrc = aClient->write (SVCHlpMsg::Error);
3118 if (VBOX_FAILURE (vrc)) break;
3119 vrc = aClient->write (errMsg);
3120 if (VBOX_FAILURE (vrc)) break;
3121 }
3122
3123 break;
3124 }
3125 default:
3126 AssertMsgFailedBreak ((
3127 "Invalid message code %d (%08lX)\n", aMsgCode, aMsgCode),
3128 VERR_GENERAL_FAILURE);
3129 }
3130
3131 LogFlowFunc (("vrc=%Vrc\n", vrc));
3132 LogFlowFuncLeave();
3133 return vrc;
3134}
3135
3136#endif /* RT_OS_WINDOWS */
3137
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