VirtualBox

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

Last change on this file since 880 was 815, checked in by vboxsync, 18 years ago

Main:

  • Return E_NOTIMPL for global USB filters and all other stuff when no VBOX_WITH_USB is defined (as in OSE).
  • Moved the USB Proxy Service check to Host to make it reusable both for global-related USB methods and for VM-related methods.

FE/Qt:

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