VirtualBox

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

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

Corrected the style in the new dbus and libhal code in Main

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