VirtualBox

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

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

Windows TAP devices: Fix for hardcoded provider name string when reloading the GUI.

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