VirtualBox

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

Last change on this file since 8429 was 8401, checked in by vboxsync, 16 years ago

USB: Some review notes and method renaming.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 108.0 KB
Line 
1/* $Id: HostImpl.cpp 8401 2008-04-26 03:48:59Z 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"))
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 Guid id (aId);
1287
1288 ComObjPtr <HostUSBDevice> device;
1289 USBDeviceList::iterator it = mUSBDevices.begin();
1290 while (!device && it != mUSBDevices.end())
1291 {
1292 if ((*it)->id() == id)
1293 device = (*it);
1294 ++ it;
1295 }
1296
1297 if (!device)
1298 return setError (E_INVALIDARG,
1299 tr ("USB device with UUID {%Vuuid} is not currently attached to the host"),
1300 id.raw());
1301
1302/** @todo r=bird this doesn't belong here but in the HostUSBDevice bit. */
1303 AutoWriteLock devLock (device);
1304
1305 if (device->isStatePending())
1306 return setError (E_INVALIDARG,
1307 tr ("USB device '%s' with UUID {%Vuuid} is busy (waiting for a pending "
1308 "state change). Please try later"),
1309 device->name().raw(), id.raw());
1310
1311 if (device->state() == USBDeviceState_NotSupported)
1312 return setError (E_INVALIDARG,
1313 tr ("USB device '%s' with UUID {%Vuuid} cannot be accessed by guest "
1314 "computers"),
1315 device->name().raw(), id.raw());
1316
1317 if (device->state() == USBDeviceState_Unavailable)
1318 return setError (E_INVALIDARG,
1319 tr ("USB device '%s' with UUID {%Vuuid} is being exclusively used by the "
1320 "host computer"),
1321 device->name().raw(), id.raw());
1322
1323 if (device->state() == USBDeviceState_Captured)
1324 {
1325 /* Machine::name() requires a read lock */
1326 AutoReadLock machLock (device->machine());
1327
1328 return setError (E_INVALIDARG,
1329 tr ("USB device '%s' with UUID {%Vuuid} is already captured by the virtual "
1330 "machine '%ls'"),
1331 device->name().raw(), id.raw(),
1332 device->machine()->name().raw());
1333 }
1334
1335 /* try to capture the device */
1336 device->requestCaptureForVM (aMachine);
1337
1338 return S_OK;
1339}
1340
1341/**
1342 * Notification from the VM process that it is going to detach (\a aDone = false)
1343 * or that is has just detach (\a aDone = true) the given USB device.
1344 *
1345 * When \a aDone = false we only inform the USB Proxy about what the vm is
1346 * up to so it doesn't get confused and create a new USB host device object
1347 * (a Darwin issue).
1348 *
1349 * When \a aDone = true we replay all filters against the given USB device
1350 * excluding filters of the machine the device is currently marked as
1351 * captured by.
1352 *
1353 * When the \a aDone = true request is completed,
1354 * IInternalSessionControl::onUSBDeviceDetach() will be called on the given
1355 * machine object.
1356 *
1357 * Called by Console from the VM process (throug IInternalMachineControl).
1358 *
1359 */
1360HRESULT Host::detachUSBDevice (SessionMachine *aMachine, INPTR GUIDPARAM aId, BOOL aDone)
1361{
1362 LogFlowThisFunc (("aMachine=%p, aId={%Vuuid}\n", aMachine, Guid (aId).raw()));
1363
1364 AutoWriteLock alock (this);
1365 CHECK_READY();
1366
1367 ComObjPtr <HostUSBDevice> device;
1368 USBDeviceList::iterator it = mUSBDevices.begin();
1369 while (!device && it != mUSBDevices.end())
1370 {
1371 if ((*it)->id() == aId)
1372 device = (*it);
1373 ++ it;
1374 }
1375
1376 ComAssertRet (!!device, E_FAIL);
1377
1378 AutoWriteLock devLock (device);
1379
1380 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d aDone=%RTbool\n",
1381 device->id().raw(), device->state(), device->isStatePending(),
1382 device->pendingState(), aDone));
1383 HRESULT rc = S_OK;
1384 if (!aDone)
1385 {
1386 if (device->isStatePending())
1387 rc = setError (E_INVALIDARG,
1388 tr ("USB device '%s' with UUID {%Vuuid} is busy (waiting for a pending "
1389 "state change). Please try later"),
1390 device->name().raw(), device->id().raw());
1391 else
1392 mUSBProxyService->detachingDevice (device);
1393 }
1394 else
1395 {
1396 if (device->isStatePending())
1397 {
1398 /* If an async detach operation is still pending (darwin), postpone
1399 the setHeld() + the re-applying of filters until it is completed.
1400 We indicate this by moving to the '*Filters' state variant. */
1401 if (device->pendingStateEx() == HostUSBDevice::kDetachingPendingAttach)
1402 device->setLogicalReconnect (HostUSBDevice::kDetachingPendingAttachFilters);
1403 else if (device->pendingStateEx() == HostUSBDevice::kDetachingPendingDetach)
1404 device->setLogicalReconnect (HostUSBDevice::kDetachingPendingDetachFilters);
1405 else
1406 {
1407 Assert (device->pendingStateEx() == HostUSBDevice::kNothingPending);
1408 rc = setError (E_INVALIDARG,
1409 tr ("USB device '%s' with UUID {%Vuuid} is busy (waiting for a pending "
1410 "state change). Please try later"),
1411 device->name().raw(), device->id().raw());
1412 }
1413 }
1414 else
1415 {
1416 ComAssertRet (device->machine() == aMachine, E_FAIL);
1417
1418 /* re-apply filters on the device before giving it back to the host */
1419 device->setHeld();
1420 rc = applyAllUSBFilters (device, aMachine);
1421 ComAssertComRC (rc);
1422 }
1423 }
1424
1425 return rc;
1426}
1427
1428/**
1429 * Asks the USB proxy service to capture all currently available USB devices
1430 * that match filters of the given machine.
1431 *
1432 * When the request is completed,
1433 * IInternalSessionControl::onUSBDeviceDetach() will be called on the given
1434 * machine object per every captured USB device.
1435 *
1436 * Called by Console from the VM process (through IInternalMachineControl)
1437 * upon VM startup.
1438 *
1439 * @note Locks this object for reading (@todo for writing now, until switched
1440 * to the new locking scheme).
1441 */
1442HRESULT Host::autoCaptureUSBDevices (SessionMachine *aMachine)
1443{
1444 LogFlowThisFunc (("aMachine=%p\n", aMachine));
1445
1446 AutoWriteLock alock (this);
1447 CHECK_READY();
1448
1449 for (USBDeviceList::iterator it = mUSBDevices.begin();
1450 it != mUSBDevices.end();
1451 ++ it)
1452 {
1453 ComObjPtr <HostUSBDevice> device = *it;
1454
1455 AutoWriteLock devLock (device);
1456
1457 /* skip pending devices */
1458 if (device->isStatePending())
1459 continue;
1460
1461 if (device->state() == USBDeviceState_Busy ||
1462 device->state() == USBDeviceState_Available ||
1463 device->state() == USBDeviceState_Held)
1464 {
1465 applyMachineUSBFilters (aMachine, device);
1466 }
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 if (!aDone)
1498 {
1499 if (!device->isStatePending())
1500 mUSBProxyService->detachingDevice (device);
1501 }
1502 else
1503 {
1504 if (!device->isStatePending())
1505 {
1506 Assert (device->state() == USBDeviceState_Captured);
1507
1508 /* re-apply filters on the device before giving it back to the
1509 * host */
1510 device->setHeld();
1511 HRESULT rc = applyAllUSBFilters (device, aMachine);
1512 AssertComRC (rc);
1513 }
1514 else if (device->pendingStateEx() == HostUSBDevice::kNothingPending)
1515 device->cancelPendingState();
1516 }
1517 }
1518 ++ it;
1519 }
1520
1521 return S_OK;
1522}
1523
1524#endif /* VBOX_HOST_USB */
1525
1526// private methods
1527////////////////////////////////////////////////////////////////////////////////
1528
1529#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1530# ifdef VBOX_USE_LIBHAL
1531/**
1532 * Helper function to query the hal subsystem for information about DVD drives attached to the
1533 * system.
1534 *
1535 * @returns true if information was successfully obtained, false otherwise
1536 * @retval list drives found will be attached to this list
1537 */
1538bool Host::getDVDInfoFromHal(std::list <ComObjPtr <HostDVDDrive> > &list)
1539{
1540 bool halSuccess = false;
1541 DBusError dbusError;
1542 if (!gLibHalCheckPresence())
1543 return false;
1544 gDBusErrorInit (&dbusError);
1545 DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError);
1546 if (dbusConnection != 0)
1547 {
1548 LibHalContext *halContext = gLibHalCtxNew();
1549 if (halContext != 0)
1550 {
1551 if (gLibHalCtxSetDBusConnection (halContext, dbusConnection))
1552 {
1553 if (gLibHalCtxInit(halContext, &dbusError))
1554 {
1555 int numDevices;
1556 char **halDevices = gLibHalFindDeviceStringMatch(halContext,
1557 "storage.drive_type", "cdrom",
1558 &numDevices, &dbusError);
1559 if (halDevices != 0)
1560 {
1561 /* Hal is installed and working, so if no devices are reported, assume
1562 that there are none. */
1563 halSuccess = true;
1564 for (int i = 0; i < numDevices; i++)
1565 {
1566 char *devNode = gLibHalDeviceGetPropertyString(halContext,
1567 halDevices[i], "block.device", &dbusError);
1568#ifdef RT_OS_SOLARIS
1569 /* The CD/DVD ioctls work only for raw device nodes. */
1570 char *tmp = getfullrawname(devNode);
1571 gLibHalFreeString(devNode);
1572 devNode = tmp;
1573#endif
1574 if (devNode != 0)
1575 {
1576// if (validateDevice(devNode, true))
1577// {
1578 Utf8Str description;
1579 char *vendor, *product;
1580 /* We do not check the error here, as this field may
1581 not even exist. */
1582 vendor = gLibHalDeviceGetPropertyString(halContext,
1583 halDevices[i], "info.vendor", 0);
1584 product = gLibHalDeviceGetPropertyString(halContext,
1585 halDevices[i], "info.product", &dbusError);
1586 if ((product != 0 && product[0] != 0))
1587 {
1588 if ((vendor != 0) && (vendor[0] != 0))
1589 {
1590 description = Utf8StrFmt ("%s %s",
1591 vendor, product);
1592 }
1593 else
1594 {
1595 description = product;
1596 }
1597 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1598 hostDVDDriveObj.createObject();
1599 hostDVDDriveObj->init (Bstr (devNode),
1600 Bstr (halDevices[i]),
1601 Bstr (description));
1602 list.push_back (hostDVDDriveObj);
1603 }
1604 else
1605 {
1606 if (product == 0)
1607 {
1608 LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
1609 halDevices[i], dbusError.name, dbusError.message));
1610 gDBusErrorFree(&dbusError);
1611 }
1612 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1613 hostDVDDriveObj.createObject();
1614 hostDVDDriveObj->init (Bstr (devNode),
1615 Bstr (halDevices[i]));
1616 list.push_back (hostDVDDriveObj);
1617 }
1618 if (vendor != 0)
1619 {
1620 gLibHalFreeString(vendor);
1621 }
1622 if (product != 0)
1623 {
1624 gLibHalFreeString(product);
1625 }
1626// }
1627// else
1628// {
1629// LogRel(("Host::COMGETTER(DVDDrives): failed to validate the block device %s as a DVD drive\n"));
1630// }
1631#ifndef RT_OS_SOLARIS
1632 gLibHalFreeString(devNode);
1633#else
1634 free(devNode);
1635#endif
1636 }
1637 else
1638 {
1639 LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
1640 halDevices[i], dbusError.name, dbusError.message));
1641 gDBusErrorFree(&dbusError);
1642 }
1643 }
1644 gLibHalFreeStringArray(halDevices);
1645 }
1646 else
1647 {
1648 LogRel(("Host::COMGETTER(DVDDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1649 gDBusErrorFree(&dbusError);
1650 }
1651 if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */
1652 {
1653 LogRel(("Host::COMGETTER(DVDDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1654 gDBusErrorFree(&dbusError);
1655 }
1656 }
1657 else
1658 {
1659 LogRel(("Host::COMGETTER(DVDDrives): failed to initialise libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1660 gDBusErrorFree(&dbusError);
1661 }
1662 gLibHalCtxFree(halContext);
1663 }
1664 else
1665 {
1666 LogRel(("Host::COMGETTER(DVDDrives): failed to set libhal connection to dbus.\n"));
1667 }
1668 }
1669 else
1670 {
1671 LogRel(("Host::COMGETTER(DVDDrives): failed to get a libhal context - out of memory?\n"));
1672 }
1673 gDBusConnectionUnref(dbusConnection);
1674 }
1675 else
1676 {
1677 LogRel(("Host::COMGETTER(DVDDrives): failed to connect to dbus. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1678 gDBusErrorFree(&dbusError);
1679 }
1680 return halSuccess;
1681}
1682
1683
1684/**
1685 * Helper function to query the hal subsystem for information about floppy drives attached to the
1686 * system.
1687 *
1688 * @returns true if information was successfully obtained, false otherwise
1689 * @retval list drives found will be attached to this list
1690 */
1691bool Host::getFloppyInfoFromHal(std::list <ComObjPtr <HostFloppyDrive> > &list)
1692{
1693 bool halSuccess = false;
1694 DBusError dbusError;
1695 if (!gLibHalCheckPresence())
1696 return false;
1697 gDBusErrorInit (&dbusError);
1698 DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError);
1699 if (dbusConnection != 0)
1700 {
1701 LibHalContext *halContext = gLibHalCtxNew();
1702 if (halContext != 0)
1703 {
1704 if (gLibHalCtxSetDBusConnection (halContext, dbusConnection))
1705 {
1706 if (gLibHalCtxInit(halContext, &dbusError))
1707 {
1708 int numDevices;
1709 char **halDevices = gLibHalFindDeviceStringMatch(halContext,
1710 "storage.drive_type", "floppy",
1711 &numDevices, &dbusError);
1712 if (halDevices != 0)
1713 {
1714 /* Hal is installed and working, so if no devices are reported, assume
1715 that there are none. */
1716 halSuccess = true;
1717 for (int i = 0; i < numDevices; i++)
1718 {
1719 char *driveType = gLibHalDeviceGetPropertyString(halContext,
1720 halDevices[i], "storage.drive_type", 0);
1721 if (driveType != 0)
1722 {
1723 if (strcmp(driveType, "floppy") != 0)
1724 {
1725 gLibHalFreeString(driveType);
1726 continue;
1727 }
1728 gLibHalFreeString(driveType);
1729 }
1730 else
1731 {
1732 /* An error occurred. The attribute "storage.drive_type"
1733 probably didn't exist. */
1734 continue;
1735 }
1736 char *devNode = gLibHalDeviceGetPropertyString(halContext,
1737 halDevices[i], "block.device", &dbusError);
1738 if (devNode != 0)
1739 {
1740// if (validateDevice(devNode, false))
1741// {
1742 Utf8Str description;
1743 char *vendor, *product;
1744 /* We do not check the error here, as this field may
1745 not even exist. */
1746 vendor = gLibHalDeviceGetPropertyString(halContext,
1747 halDevices[i], "info.vendor", 0);
1748 product = gLibHalDeviceGetPropertyString(halContext,
1749 halDevices[i], "info.product", &dbusError);
1750 if ((product != 0) && (product[0] != 0))
1751 {
1752 if ((vendor != 0) && (vendor[0] != 0))
1753 {
1754 description = Utf8StrFmt ("%s %s",
1755 vendor, product);
1756 }
1757 else
1758 {
1759 description = product;
1760 }
1761 ComObjPtr <HostFloppyDrive> hostFloppyDrive;
1762 hostFloppyDrive.createObject();
1763 hostFloppyDrive->init (Bstr (devNode),
1764 Bstr (halDevices[i]),
1765 Bstr (description));
1766 list.push_back (hostFloppyDrive);
1767 }
1768 else
1769 {
1770 if (product == 0)
1771 {
1772 LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
1773 halDevices[i], dbusError.name, dbusError.message));
1774 gDBusErrorFree(&dbusError);
1775 }
1776 ComObjPtr <HostFloppyDrive> hostFloppyDrive;
1777 hostFloppyDrive.createObject();
1778 hostFloppyDrive->init (Bstr (devNode),
1779 Bstr (halDevices[i]));
1780 list.push_back (hostFloppyDrive);
1781 }
1782 if (vendor != 0)
1783 {
1784 gLibHalFreeString(vendor);
1785 }
1786 if (product != 0)
1787 {
1788 gLibHalFreeString(product);
1789 }
1790// }
1791// else
1792// {
1793// LogRel(("Host::COMGETTER(FloppyDrives): failed to validate the block device %s as a floppy drive\n"));
1794// }
1795 gLibHalFreeString(devNode);
1796 }
1797 else
1798 {
1799 LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
1800 halDevices[i], dbusError.name, dbusError.message));
1801 gDBusErrorFree(&dbusError);
1802 }
1803 }
1804 gLibHalFreeStringArray(halDevices);
1805 }
1806 else
1807 {
1808 LogRel(("Host::COMGETTER(FloppyDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1809 gDBusErrorFree(&dbusError);
1810 }
1811 if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */
1812 {
1813 LogRel(("Host::COMGETTER(FloppyDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1814 gDBusErrorFree(&dbusError);
1815 }
1816 }
1817 else
1818 {
1819 LogRel(("Host::COMGETTER(FloppyDrives): failed to initialise libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1820 gDBusErrorFree(&dbusError);
1821 }
1822 gLibHalCtxFree(halContext);
1823 }
1824 else
1825 {
1826 LogRel(("Host::COMGETTER(FloppyDrives): failed to set libhal connection to dbus.\n"));
1827 }
1828 }
1829 else
1830 {
1831 LogRel(("Host::COMGETTER(FloppyDrives): failed to get a libhal context - out of memory?\n"));
1832 }
1833 gDBusConnectionUnref(dbusConnection);
1834 }
1835 else
1836 {
1837 LogRel(("Host::COMGETTER(FloppyDrives): failed to connect to dbus. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1838 gDBusErrorFree(&dbusError);
1839 }
1840 return halSuccess;
1841}
1842# endif /* VBOX_USE_HAL defined */
1843
1844/**
1845 * Helper function to parse the given mount file and add found entries
1846 */
1847void Host::parseMountTable(char *mountTable, std::list <ComObjPtr <HostDVDDrive> > &list)
1848{
1849#ifdef RT_OS_LINUX
1850 FILE *mtab = setmntent(mountTable, "r");
1851 if (mtab)
1852 {
1853 struct mntent *mntent;
1854 char *mnt_type;
1855 char *mnt_dev;
1856 char *tmp;
1857 while ((mntent = getmntent(mtab)))
1858 {
1859 mnt_type = (char*)malloc(strlen(mntent->mnt_type) + 1);
1860 mnt_dev = (char*)malloc(strlen(mntent->mnt_fsname) + 1);
1861 strcpy(mnt_type, mntent->mnt_type);
1862 strcpy(mnt_dev, mntent->mnt_fsname);
1863 // supermount fs case
1864 if (strcmp(mnt_type, "supermount") == 0)
1865 {
1866 tmp = strstr(mntent->mnt_opts, "fs=");
1867 if (tmp)
1868 {
1869 free(mnt_type);
1870 mnt_type = strdup(tmp + strlen("fs="));
1871 if (mnt_type)
1872 {
1873 tmp = strchr(mnt_type, ',');
1874 if (tmp)
1875 *tmp = '\0';
1876 }
1877 }
1878 tmp = strstr(mntent->mnt_opts, "dev=");
1879 if (tmp)
1880 {
1881 free(mnt_dev);
1882 mnt_dev = strdup(tmp + strlen("dev="));
1883 if (mnt_dev)
1884 {
1885 tmp = strchr(mnt_dev, ',');
1886 if (tmp)
1887 *tmp = '\0';
1888 }
1889 }
1890 }
1891 // use strstr here to cover things fs types like "udf,iso9660"
1892 if (strstr(mnt_type, "iso9660") == 0)
1893 {
1894 /** @todo check whether we've already got the drive in our list! */
1895 if (validateDevice(mnt_dev, true))
1896 {
1897 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1898 hostDVDDriveObj.createObject();
1899 hostDVDDriveObj->init (Bstr (mnt_dev));
1900 list.push_back (hostDVDDriveObj);
1901 }
1902 }
1903 free(mnt_dev);
1904 free(mnt_type);
1905 }
1906 endmntent(mtab);
1907 }
1908#else // RT_OS_SOLARIS
1909 FILE *mntFile = fopen(mountTable, "r");
1910 if (mntFile)
1911 {
1912 struct mnttab mntTab;
1913 while (getmntent(mntFile, &mntTab) == 0)
1914 {
1915 char *mountName = strdup(mntTab.mnt_special);
1916 char *mountPoint = strdup(mntTab.mnt_mountp);
1917 char *mountFSType = strdup(mntTab.mnt_fstype);
1918
1919 // skip devices we are not interested in
1920 if ((*mountName && mountName[0] == '/') && // skip 'fake' devices (like -hosts, proc, fd, swap)
1921 (*mountFSType && (strcmp(mountFSType, "devfs") != 0 && // skip devfs (i.e. /devices)
1922 strcmp(mountFSType, "dev") != 0 && // skip dev (i.e. /dev)
1923 strcmp(mountFSType, "lofs") != 0)) && // skip loop-back file-system (lofs)
1924 (*mountPoint && strcmp(mountPoint, "/") != 0)) // skip point '/' (Can CD/DVD be mounted at '/' ???)
1925 {
1926 char *rawDevName = getfullrawname(mountName);
1927 if (validateDevice(rawDevName, true))
1928 {
1929 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1930 hostDVDDriveObj.createObject();
1931 hostDVDDriveObj->init (Bstr (rawDevName));
1932 list.push_back (hostDVDDriveObj);
1933 }
1934 free(rawDevName);
1935 }
1936
1937 free(mountName);
1938 free(mountPoint);
1939 free(mountFSType);
1940 }
1941
1942 fclose(mntFile);
1943 }
1944#endif
1945}
1946
1947/**
1948 * Helper function to check whether the given device node is a valid drive
1949 */
1950bool Host::validateDevice(const char *deviceNode, bool isCDROM)
1951{
1952 struct stat statInfo;
1953 bool retValue = false;
1954
1955 // sanity check
1956 if (!deviceNode)
1957 {
1958 return false;
1959 }
1960
1961 // first a simple stat() call
1962 if (stat(deviceNode, &statInfo) < 0)
1963 {
1964 return false;
1965 } else
1966 {
1967 if (isCDROM)
1968 {
1969 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
1970 {
1971 int fileHandle;
1972 // now try to open the device
1973 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
1974 if (fileHandle >= 0)
1975 {
1976 cdrom_subchnl cdChannelInfo;
1977 cdChannelInfo.cdsc_format = CDROM_MSF;
1978 // this call will finally reveal the whole truth
1979#ifdef RT_OS_LINUX
1980 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
1981 (errno == EIO) || (errno == ENOENT) ||
1982 (errno == EINVAL) || (errno == ENOMEDIUM))
1983#else
1984 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
1985 (errno == EIO) || (errno == ENOENT) ||
1986 (errno == EINVAL))
1987#endif
1988 {
1989 retValue = true;
1990 }
1991 close(fileHandle);
1992 }
1993 }
1994 } else
1995 {
1996 // floppy case
1997 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
1998 {
1999 /// @todo do some more testing, maybe a nice IOCTL!
2000 retValue = true;
2001 }
2002 }
2003 }
2004 return retValue;
2005}
2006#endif // RT_OS_LINUX || RT_OS_SOLARIS
2007
2008#ifdef VBOX_WITH_USB
2009
2010/**
2011 * Applies all (golbal and VM) filters to the given USB device. The device
2012 * must be either a newly attached device or a device released by a VM.
2013 *
2014 * This method will request the USB proxy service to release the device (give
2015 * it back to the host) if none of the global or VM filters want to capture
2016 * the device.
2017 *
2018 * @param aDevice USB device to apply filters to.
2019 * @param aMachine Machine the device was released by or @c NULL.
2020 *
2021 * @note the method must be called from under this object's write lock and
2022 * from the aDevice's write lock.
2023 */
2024HRESULT Host::applyAllUSBFilters (ComObjPtr <HostUSBDevice> &aDevice,
2025 SessionMachine *aMachine /* = NULL */)
2026{
2027 LogFlowThisFunc (("\n"));
2028
2029 /// @todo must check for read lock, it's enough here
2030 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
2031
2032 AssertReturn (aDevice->isWriteLockOnCurrentThread(), E_FAIL);
2033
2034 AssertReturn (aDevice->state() != USBDeviceState_Captured, E_FAIL);
2035
2036 AssertReturn (aDevice->isStatePending() == false, E_FAIL);
2037
2038 /* ignore unsupported devices */
2039 if (aDevice->state() == USBDeviceState_NotSupported)
2040 return S_OK;
2041 /* ignore unavailable devices as well */
2042 if (aDevice->state() == USBDeviceState_Unavailable)
2043 return S_OK;
2044
2045 VirtualBox::SessionMachineVector machines;
2046 mParent->getOpenedMachines (machines);
2047
2048 /// @todo it may be better to take a copy of filters to iterate and leave
2049 /// the host lock before calling HostUSBDevice:requestCaptureForVM() (which
2050 /// may call the VM process).
2051 /// Moving this matching into USBProxyService at the same time would be
2052 /// nice too, that'll simplify the locking a bit more.
2053
2054 /* apply global filters */
2055 USBDeviceFilterList::const_iterator it = mUSBDeviceFilters.begin();
2056 for (; it != mUSBDeviceFilters.end(); ++ it)
2057 {
2058 AutoWriteLock filterLock (*it);
2059 const HostUSBDeviceFilter::Data &data = (*it)->data();
2060 if (aDevice->isMatch (data))
2061 {
2062 USBDeviceFilterAction_T action = USBDeviceFilterAction_Null;
2063 (*it)->COMGETTER (Action) (&action);
2064 if (action == USBDeviceFilterAction_Ignore)
2065 {
2066 /* request to give the device back to the host */
2067 aDevice->requestReleaseToHost();
2068 /* nothing to do any more */
2069 return S_OK;
2070 }
2071 if (action == USBDeviceFilterAction_Hold)
2072 break;
2073 }
2074 }
2075
2076 /* apply machine filters */
2077 size_t i = 0;
2078 for (; i < machines.size(); ++ i)
2079 {
2080 /* skip the machine the device was just detached from */
2081 if (aMachine && machines [i] == aMachine)
2082 continue;
2083
2084 if (applyMachineUSBFilters (machines [i], aDevice))
2085 break;
2086 }
2087
2088 if (i == machines.size())
2089 {
2090 /* no matched machine filters, check what to do */
2091 if (it == mUSBDeviceFilters.end())
2092 {
2093 /* no any filter matched at all */
2094 /* request to give the device back to the host */
2095 aDevice->requestReleaseToHost();
2096 }
2097 else
2098 {
2099 /* there was a global Hold filter */
2100 aDevice->requestHold();
2101 }
2102 }
2103
2104 return S_OK;
2105}
2106
2107/**
2108 * Runs through filters of the given machine and asks the USB proxy service
2109 * to capture the given USB device when there is a match.
2110 *
2111 * @param aMachine Machine whose filters are to be run.
2112 * @param aDevice USB device, a candidate for auto-capturing.
2113 * @return @c true if there was a match and @c false otherwise.
2114 *
2115 * @note the method must be called from under this object's write lock and
2116 * from the aDevice's write lock.
2117 *
2118 * @note Locks aMachine for reading.
2119 */
2120bool Host::applyMachineUSBFilters (SessionMachine *aMachine,
2121 ComObjPtr <HostUSBDevice> &aDevice)
2122{
2123 LogFlowThisFunc (("\n"));
2124
2125 AssertReturn (aMachine, false);
2126
2127 /// @todo must check for read lock, it's enough here
2128 AssertReturn (isWriteLockOnCurrentThread(), false);
2129
2130 AssertReturn (aDevice->isWriteLockOnCurrentThread(), false);
2131
2132 AssertReturn (aDevice->state() != USBDeviceState_NotSupported, false);
2133 AssertReturn (aDevice->state() != USBDeviceState_Unavailable, false);
2134
2135 AssertReturn (aDevice->isStatePending() == false, false);
2136
2137 ULONG maskedIfs;
2138 bool hasMatch = aMachine->hasMatchingUSBFilter (aDevice, &maskedIfs);
2139
2140 if (hasMatch)
2141 {
2142 /** @todo r=bird: this is wrong as requestAttachToVM may return false for different reasons that we. */
2143 /* try to capture the device */
2144 return aDevice->requestCaptureForVM (aMachine, maskedIfs);
2145 }
2146
2147 return hasMatch;
2148}
2149
2150/**
2151 * Called by USB proxy service when a new device is physically attached
2152 * to the host.
2153 *
2154 * @param aDevice Pointer to the device which has been attached.
2155 */
2156void Host::onUSBDeviceAttached (HostUSBDevice *aDevice)
2157{
2158 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2159
2160 AssertReturnVoid (aDevice);
2161
2162 AssertReturnVoid (isWriteLockOnCurrentThread());
2163 AssertReturnVoid (aDevice->isWriteLockOnCurrentThread());
2164
2165 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2166 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2167 aDevice->pendingState()));
2168
2169 Assert (aDevice->isStatePending() == false);
2170
2171 /* add to the collecion */
2172 mUSBDevices.push_back (aDevice);
2173
2174 /* apply all filters */
2175 ComObjPtr <HostUSBDevice> device (aDevice);
2176 HRESULT rc = applyAllUSBFilters (device);
2177 AssertComRC (rc);
2178}
2179
2180/**
2181 * Called by USB proxy service when the device is physically detached
2182 * from the host.
2183 *
2184 * @param aDevice Pointer to the device which has been detached.
2185 */
2186void Host::onUSBDeviceDetached (HostUSBDevice *aDevice)
2187{
2188 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2189
2190 AssertReturnVoid (aDevice);
2191
2192 AssertReturnVoid (isWriteLockOnCurrentThread());
2193 AssertReturnVoid (aDevice->isWriteLockOnCurrentThread());
2194
2195 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2196 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2197 aDevice->pendingState()));
2198
2199 Guid id = aDevice->id();
2200
2201 ComObjPtr <HostUSBDevice> device;
2202 Host::USBDeviceList::iterator it = mUSBDevices.begin();
2203 while (it != mUSBDevices.end())
2204 {
2205 if ((*it)->id() == id)
2206 {
2207 device = (*it);
2208 break;
2209 }
2210 ++ it;
2211 }
2212
2213 AssertReturnVoid (!!device);
2214
2215 /* remove from the collecion */
2216 mUSBDevices.erase (it);
2217
2218 /* Detach the device from any machine currently using it,
2219 reset all data and uninitialize the device object. */
2220 device->onDetachedPhys();
2221}
2222
2223/**
2224 * Called by USB proxy service when the state of the device has changed
2225 * either because of the state change request or because of some external
2226 * interaction.
2227 *
2228 * @param aDevice The device in question.
2229 */
2230void Host::onUSBDeviceStateChanged (HostUSBDevice *aDevice)
2231{
2232 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2233
2234 AssertReturnVoid (aDevice);
2235
2236 AssertReturnVoid (isWriteLockOnCurrentThread());
2237 AssertReturnVoid (aDevice->isWriteLockOnCurrentThread());
2238
2239 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2240 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2241 aDevice->pendingState()));
2242
2243
2244 ComObjPtr <HostUSBDevice> device (aDevice);
2245 if (device->isStatePending())
2246 {
2247 /* it was a state change request */
2248 if (device->pendingStateEx() == HostUSBDevice::kDetachingPendingAttachFilters)
2249 {
2250 /* The device has completed an asynchronous detach operation, subject
2251 it to the filters and such if the current state permits this.
2252 (handlePendingStateChange will disassociate itself from the machine.) */
2253 ComObjPtr <SessionMachine> machine (device->machine());
2254 device->handlePendingStateChange();
2255 if (device->state() == USBDeviceState_Captured)
2256 {
2257 Log (("USB: running filters on async detached device\n"));
2258 device->setHeld();
2259 HRESULT rc = applyAllUSBFilters (device, machine);
2260 AssertComRC (rc);
2261 }
2262 else
2263 Log (("USB: async detached devices reappeared in stated %d instead of %d!\n",
2264 device->state(), USBDeviceState_Captured));
2265 }
2266 else
2267 device->handlePendingStateChange();
2268 }
2269 else if ( device->state() == USBDeviceState_Available
2270 || device->state() == USBDeviceState_Busy)
2271 {
2272 /* The device has gone from being unavailable (not subject to filters) to being
2273 available / busy. This transition can be triggered by udevd or manual
2274 permission changes on Linux. On all systems may be triggered by the host
2275 ceasing to use the device - like unmounting an MSD in the Finder or invoking
2276 the "Safely remove XXXX" stuff on Windows (perhaps). */
2277 HRESULT rc = applyAllUSBFilters (device);
2278 AssertComRC (rc);
2279 }
2280 else
2281 {
2282 /* some external state change */
2283
2284 /// @todo re-run all USB filters probably
2285 AssertFailed();
2286 }
2287}
2288#endif /* VBOX_WITH_USB */
2289
2290/**
2291 * Checks for the presense and status of the USB Proxy Service.
2292 * Returns S_OK when the Proxy is present and OK, or E_FAIL and a
2293 * corresponding error message otherwise. Intended to be used by methods
2294 * that rely on the Proxy Service availability.
2295 *
2296 * @note This method may return a warning result code. It is recommended to use
2297 * MultiError to store the return value.
2298 *
2299 * @note Locks this object for reading.
2300 */
2301HRESULT Host::checkUSBProxyService()
2302{
2303#ifdef VBOX_WITH_USB
2304 AutoWriteLock alock (this);
2305 CHECK_READY();
2306
2307 AssertReturn (mUSBProxyService, E_FAIL);
2308 if (!mUSBProxyService->isActive())
2309 {
2310 /* disable the USB controller completely to avoid assertions if the
2311 * USB proxy service could not start. */
2312
2313 if (mUSBProxyService->getLastError() == VERR_FILE_NOT_FOUND)
2314 return setWarning (E_FAIL,
2315 tr ("Could not load the Host USB Proxy Service (%Vrc). "
2316 "The service might be not installed on the host computer"),
2317 mUSBProxyService->getLastError());
2318 if (mUSBProxyService->getLastError() == VINF_SUCCESS)
2319 return setWarning (E_FAIL,
2320 tr ("The USB Proxy Service has not yet been ported to this host"));
2321 return setWarning (E_FAIL,
2322 tr ("Could not load the Host USB Proxy service (%Vrc)"),
2323 mUSBProxyService->getLastError());
2324 }
2325
2326 return S_OK;
2327#else
2328 return E_NOTIMPL;
2329#endif
2330}
2331
2332#ifdef RT_OS_WINDOWS
2333
2334/* The original source of the VBoxTAP adapter creation/destruction code has the following copyright */
2335/*
2336 Copyright 2004 by the Massachusetts Institute of Technology
2337
2338 All rights reserved.
2339
2340 Permission to use, copy, modify, and distribute this software and its
2341 documentation for any purpose and without fee is hereby granted,
2342 provided that the above copyright notice appear in all copies and that
2343 both that copyright notice and this permission notice appear in
2344 supporting documentation, and that the name of the Massachusetts
2345 Institute of Technology (M.I.T.) not be used in advertising or publicity
2346 pertaining to distribution of the software without specific, written
2347 prior permission.
2348
2349 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
2350 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
2351 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
2352 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
2353 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2354 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2355 SOFTWARE.
2356*/
2357
2358
2359#define NETSHELL_LIBRARY _T("netshell.dll")
2360
2361/**
2362 * Use the IShellFolder API to rename the connection.
2363 */
2364static HRESULT rename_shellfolder (PCWSTR wGuid, PCWSTR wNewName)
2365{
2366 /* This is the GUID for the network connections folder. It is constant.
2367 * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */
2368 const GUID CLSID_NetworkConnections = {
2369 0x7007ACC7, 0x3202, 0x11D1, {
2370 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
2371 }
2372 };
2373
2374 LPITEMIDLIST pidl = NULL;
2375 IShellFolder *pShellFolder = NULL;
2376 HRESULT hr;
2377
2378 /* Build the display name in the form "::{GUID}". */
2379 if (wcslen (wGuid) >= MAX_PATH)
2380 return E_INVALIDARG;
2381 WCHAR szAdapterGuid[MAX_PATH + 2] = {0};
2382 swprintf (szAdapterGuid, L"::%ls", wGuid);
2383
2384 /* Create an instance of the network connections folder. */
2385 hr = CoCreateInstance (CLSID_NetworkConnections, NULL,
2386 CLSCTX_INPROC_SERVER, IID_IShellFolder,
2387 reinterpret_cast <LPVOID *> (&pShellFolder));
2388 /* Parse the display name. */
2389 if (SUCCEEDED (hr))
2390 {
2391 hr = pShellFolder->ParseDisplayName (NULL, NULL, szAdapterGuid, NULL,
2392 &pidl, NULL);
2393 }
2394 if (SUCCEEDED (hr))
2395 {
2396 hr = pShellFolder->SetNameOf (NULL, pidl, wNewName, SHGDN_NORMAL,
2397 &pidl);
2398 }
2399
2400 CoTaskMemFree (pidl);
2401
2402 if (pShellFolder)
2403 pShellFolder->Release();
2404
2405 return hr;
2406}
2407
2408extern "C" HRESULT RenameConnection (PCWSTR GuidString, PCWSTR NewName)
2409{
2410 typedef HRESULT (WINAPI *lpHrRenameConnection) (const GUID *, PCWSTR);
2411 lpHrRenameConnection RenameConnectionFunc = NULL;
2412 HRESULT status;
2413
2414 /* First try the IShellFolder interface, which was unimplemented
2415 * for the network connections folder before XP. */
2416 status = rename_shellfolder (GuidString, NewName);
2417 if (status == E_NOTIMPL)
2418 {
2419/** @todo that code doesn't seem to work! */
2420 /* The IShellFolder interface is not implemented on this platform.
2421 * Try the (undocumented) HrRenameConnection API in the netshell
2422 * library. */
2423 CLSID clsid;
2424 HINSTANCE hNetShell;
2425 status = CLSIDFromString ((LPOLESTR) GuidString, &clsid);
2426 if (FAILED(status))
2427 return E_FAIL;
2428 hNetShell = LoadLibrary (NETSHELL_LIBRARY);
2429 if (hNetShell == NULL)
2430 return E_FAIL;
2431 RenameConnectionFunc =
2432 (lpHrRenameConnection) GetProcAddress (hNetShell,
2433 "HrRenameConnection");
2434 if (RenameConnectionFunc == NULL)
2435 {
2436 FreeLibrary (hNetShell);
2437 return E_FAIL;
2438 }
2439 status = RenameConnectionFunc (&clsid, NewName);
2440 FreeLibrary (hNetShell);
2441 }
2442 if (FAILED (status))
2443 return status;
2444
2445 return S_OK;
2446}
2447
2448#define DRIVERHWID _T("vboxtap")
2449
2450#define SetErrBreak(strAndArgs) \
2451 if (1) { \
2452 aErrMsg = Utf8StrFmt strAndArgs; vrc = VERR_GENERAL_FAILURE; break; \
2453 } else do {} while (0)
2454
2455/* static */
2456int Host::createNetworkInterface (SVCHlpClient *aClient,
2457 const Utf8Str &aName,
2458 Guid &aGUID, Utf8Str &aErrMsg)
2459{
2460 LogFlowFuncEnter();
2461 LogFlowFunc (("Network connection name = '%s'\n", aName.raw()));
2462
2463 AssertReturn (aClient, VERR_INVALID_POINTER);
2464 AssertReturn (!aName.isNull(), VERR_INVALID_PARAMETER);
2465
2466 int vrc = VINF_SUCCESS;
2467
2468 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2469 SP_DEVINFO_DATA DeviceInfoData;
2470 DWORD ret = 0;
2471 BOOL found = FALSE;
2472 BOOL registered = FALSE;
2473 BOOL destroyList = FALSE;
2474 TCHAR pCfgGuidString [50];
2475
2476 do
2477 {
2478 BOOL ok;
2479 GUID netGuid;
2480 SP_DRVINFO_DATA DriverInfoData;
2481 SP_DEVINSTALL_PARAMS DeviceInstallParams;
2482 TCHAR className [MAX_PATH];
2483 DWORD index = 0;
2484 PSP_DRVINFO_DETAIL_DATA pDriverInfoDetail;
2485 /* for our purposes, 2k buffer is more
2486 * than enough to obtain the hardware ID
2487 * of the VBoxTAP driver. */
2488 DWORD detailBuf [2048];
2489
2490 HKEY hkey = NULL;
2491 DWORD cbSize;
2492 DWORD dwValueType;
2493
2494 /* initialize the structure size */
2495 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
2496 DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
2497
2498 /* copy the net class GUID */
2499 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
2500
2501 /* create an empty device info set associated with the net class GUID */
2502 hDeviceInfo = SetupDiCreateDeviceInfoList (&netGuid, NULL);
2503 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2504 SetErrBreak (("SetupDiCreateDeviceInfoList failed (0x%08X)",
2505 GetLastError()));
2506
2507 /* get the class name from GUID */
2508 ok = SetupDiClassNameFromGuid (&netGuid, className, MAX_PATH, NULL);
2509 if (!ok)
2510 SetErrBreak (("SetupDiClassNameFromGuid failed (0x%08X)",
2511 GetLastError()));
2512
2513 /* create a device info element and add the new device instance
2514 * key to registry */
2515 ok = SetupDiCreateDeviceInfo (hDeviceInfo, className, &netGuid, NULL, NULL,
2516 DICD_GENERATE_ID, &DeviceInfoData);
2517 if (!ok)
2518 SetErrBreak (("SetupDiCreateDeviceInfo failed (0x%08X)",
2519 GetLastError()));
2520
2521 /* select the newly created device info to be the currently
2522 selected member */
2523 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2524 if (!ok)
2525 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2526 GetLastError()));
2527
2528 /* build a list of class drivers */
2529 ok = SetupDiBuildDriverInfoList (hDeviceInfo, &DeviceInfoData,
2530 SPDIT_CLASSDRIVER);
2531 if (!ok)
2532 SetErrBreak (("SetupDiBuildDriverInfoList failed (0x%08X)",
2533 GetLastError()));
2534
2535 destroyList = TRUE;
2536
2537 /* enumerate the driver info list */
2538 while (TRUE)
2539 {
2540 BOOL ret;
2541
2542 ret = SetupDiEnumDriverInfo (hDeviceInfo, &DeviceInfoData,
2543 SPDIT_CLASSDRIVER, index, &DriverInfoData);
2544
2545 /* if the function failed and GetLastError() returned
2546 * ERROR_NO_MORE_ITEMS, then we have reached the end of the
2547 * list. Othewise there was something wrong with this
2548 * particular driver. */
2549 if (!ret)
2550 {
2551 if(GetLastError() == ERROR_NO_MORE_ITEMS)
2552 break;
2553 else
2554 {
2555 index++;
2556 continue;
2557 }
2558 }
2559
2560 pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA) detailBuf;
2561 pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
2562
2563 /* if we successfully find the hardware ID and it turns out to
2564 * be the one for the loopback driver, then we are done. */
2565 if (SetupDiGetDriverInfoDetail (hDeviceInfo,
2566 &DeviceInfoData,
2567 &DriverInfoData,
2568 pDriverInfoDetail,
2569 sizeof (detailBuf),
2570 NULL))
2571 {
2572 TCHAR * t;
2573
2574 /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the
2575 * whole list and see if there is a match somewhere. */
2576 t = pDriverInfoDetail->HardwareID;
2577 while (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2578 {
2579 if (!_tcsicmp(t, DRIVERHWID))
2580 break;
2581
2582 t += _tcslen(t) + 1;
2583 }
2584
2585 if (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2586 {
2587 found = TRUE;
2588 break;
2589 }
2590 }
2591
2592 index ++;
2593 }
2594
2595 if (!found)
2596 SetErrBreak ((tr ("Could not find Host Interface Networking driver! "
2597 "Please reinstall")));
2598
2599 /* set the loopback driver to be the currently selected */
2600 ok = SetupDiSetSelectedDriver (hDeviceInfo, &DeviceInfoData,
2601 &DriverInfoData);
2602 if (!ok)
2603 SetErrBreak (("SetupDiSetSelectedDriver failed (0x%08X)",
2604 GetLastError()));
2605
2606 /* register the phantom device to prepare for install */
2607 ok = SetupDiCallClassInstaller (DIF_REGISTERDEVICE, hDeviceInfo,
2608 &DeviceInfoData);
2609 if (!ok)
2610 SetErrBreak (("SetupDiCallClassInstaller failed (0x%08X)",
2611 GetLastError()));
2612
2613 /* registered, but remove if errors occur in the following code */
2614 registered = TRUE;
2615
2616 /* ask the installer if we can install the device */
2617 ok = SetupDiCallClassInstaller (DIF_ALLOW_INSTALL, hDeviceInfo,
2618 &DeviceInfoData);
2619 if (!ok)
2620 {
2621 if (GetLastError() != ERROR_DI_DO_DEFAULT)
2622 SetErrBreak (("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (0x%08X)",
2623 GetLastError()));
2624 /* that's fine */
2625 }
2626
2627 /* install the files first */
2628 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES, hDeviceInfo,
2629 &DeviceInfoData);
2630 if (!ok)
2631 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (0x%08X)",
2632 GetLastError()));
2633
2634 /* get the device install parameters and disable filecopy */
2635 DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
2636 ok = SetupDiGetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2637 &DeviceInstallParams);
2638 if (ok)
2639 {
2640 DeviceInstallParams.Flags |= DI_NOFILECOPY;
2641 ok = SetupDiSetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2642 &DeviceInstallParams);
2643 if (!ok)
2644 SetErrBreak (("SetupDiSetDeviceInstallParams failed (0x%08X)",
2645 GetLastError()));
2646 }
2647
2648 /*
2649 * Register any device-specific co-installers for this device,
2650 */
2651
2652 ok = SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS,
2653 hDeviceInfo,
2654 &DeviceInfoData);
2655 if (!ok)
2656 SetErrBreak (("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (0x%08X)",
2657 GetLastError()));
2658
2659 /*
2660 * install any installer-specified interfaces.
2661 * and then do the real install
2662 */
2663 ok = SetupDiCallClassInstaller (DIF_INSTALLINTERFACES,
2664 hDeviceInfo,
2665 &DeviceInfoData);
2666 if (!ok)
2667 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (0x%08X)",
2668 GetLastError()));
2669
2670 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICE,
2671 hDeviceInfo,
2672 &DeviceInfoData);
2673 if (!ok)
2674 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (0x%08X)",
2675 GetLastError()));
2676
2677 /* Figure out NetCfgInstanceId */
2678 hkey = SetupDiOpenDevRegKey (hDeviceInfo,
2679 &DeviceInfoData,
2680 DICS_FLAG_GLOBAL,
2681 0,
2682 DIREG_DRV,
2683 KEY_READ);
2684 if (hkey == INVALID_HANDLE_VALUE)
2685 SetErrBreak (("SetupDiOpenDevRegKey failed (0x%08X)",
2686 GetLastError()));
2687
2688 cbSize = sizeof (pCfgGuidString);
2689 DWORD ret;
2690 ret = RegQueryValueEx (hkey, _T ("NetCfgInstanceId"), NULL,
2691 &dwValueType, (LPBYTE) pCfgGuidString, &cbSize);
2692 RegCloseKey (hkey);
2693
2694 ret = RenameConnection (pCfgGuidString, Bstr (aName));
2695 if (FAILED (ret))
2696 SetErrBreak (("Failed to set interface name (ret=0x%08X, "
2697 "pCfgGuidString='%ls', cbSize=%d)",
2698 ret, pCfgGuidString, cbSize));
2699 }
2700 while (0);
2701
2702 /*
2703 * cleanup
2704 */
2705
2706 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2707 {
2708 /* an error has occured, but the device is registered, we must remove it */
2709 if (ret != 0 && registered)
2710 SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2711
2712 found = SetupDiDeleteDeviceInfo (hDeviceInfo, &DeviceInfoData);
2713
2714 /* destroy the driver info list */
2715 if (destroyList)
2716 SetupDiDestroyDriverInfoList (hDeviceInfo, &DeviceInfoData,
2717 SPDIT_CLASSDRIVER);
2718 /* clean up the device info set */
2719 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2720 }
2721
2722 /* return the network connection GUID on success */
2723 if (VBOX_SUCCESS (vrc))
2724 {
2725 /* remove the curly bracket at the end */
2726 pCfgGuidString [_tcslen (pCfgGuidString) - 1] = '\0';
2727 LogFlowFunc (("Network connection GUID string = {%ls}\n", pCfgGuidString + 1));
2728
2729 aGUID = Guid (Utf8Str (pCfgGuidString + 1));
2730 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2731 Assert (!aGUID.isEmpty());
2732 }
2733
2734 LogFlowFunc (("vrc=%Vrc\n", vrc));
2735 LogFlowFuncLeave();
2736 return vrc;
2737}
2738
2739/* static */
2740int Host::removeNetworkInterface (SVCHlpClient *aClient,
2741 const Guid &aGUID,
2742 Utf8Str &aErrMsg)
2743{
2744 LogFlowFuncEnter();
2745 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2746
2747 AssertReturn (aClient, VERR_INVALID_POINTER);
2748 AssertReturn (!aGUID.isEmpty(), VERR_INVALID_PARAMETER);
2749
2750 int vrc = VINF_SUCCESS;
2751
2752 do
2753 {
2754 TCHAR lszPnPInstanceId [512] = {0};
2755
2756 /* We have to find the device instance ID through a registry search */
2757
2758 HKEY hkeyNetwork = 0;
2759 HKEY hkeyConnection = 0;
2760
2761 do
2762 {
2763 char strRegLocation [256];
2764 sprintf (strRegLocation,
2765 "SYSTEM\\CurrentControlSet\\Control\\Network\\"
2766 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\{%s}",
2767 aGUID.toString().raw());
2768 LONG status;
2769 status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, strRegLocation, 0,
2770 KEY_READ, &hkeyNetwork);
2771 if ((status != ERROR_SUCCESS) || !hkeyNetwork)
2772 SetErrBreak ((
2773 tr ("Host interface network is not found in registry (%s) [1]"),
2774 strRegLocation));
2775
2776 status = RegOpenKeyExA (hkeyNetwork, "Connection", 0,
2777 KEY_READ, &hkeyConnection);
2778 if ((status != ERROR_SUCCESS) || !hkeyConnection)
2779 SetErrBreak ((
2780 tr ("Host interface network is not found in registry (%s) [2]"),
2781 strRegLocation));
2782
2783 DWORD len = sizeof (lszPnPInstanceId);
2784 DWORD dwKeyType;
2785 status = RegQueryValueExW (hkeyConnection, L"PnPInstanceID", NULL,
2786 &dwKeyType, (LPBYTE) lszPnPInstanceId, &len);
2787 if ((status != ERROR_SUCCESS) || (dwKeyType != REG_SZ))
2788 SetErrBreak ((
2789 tr ("Host interface network is not found in registry (%s) [3]"),
2790 strRegLocation));
2791 }
2792 while (0);
2793
2794 if (hkeyConnection)
2795 RegCloseKey (hkeyConnection);
2796 if (hkeyNetwork)
2797 RegCloseKey (hkeyNetwork);
2798
2799 if (VBOX_FAILURE (vrc))
2800 break;
2801
2802 /*
2803 * Now we are going to enumerate all network devices and
2804 * wait until we encounter the right device instance ID
2805 */
2806
2807 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2808
2809 do
2810 {
2811 BOOL ok;
2812 DWORD ret = 0;
2813 GUID netGuid;
2814 SP_DEVINFO_DATA DeviceInfoData;
2815 DWORD index = 0;
2816 BOOL found = FALSE;
2817 DWORD size = 0;
2818
2819 /* initialize the structure size */
2820 DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
2821
2822 /* copy the net class GUID */
2823 memcpy (&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2824
2825 /* return a device info set contains all installed devices of the Net class */
2826 hDeviceInfo = SetupDiGetClassDevs (&netGuid, NULL, NULL, DIGCF_PRESENT);
2827
2828 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2829 SetErrBreak (("SetupDiGetClassDevs failed (0x%08X)", GetLastError()));
2830
2831 /* enumerate the driver info list */
2832 while (TRUE)
2833 {
2834 TCHAR *deviceHwid;
2835
2836 ok = SetupDiEnumDeviceInfo (hDeviceInfo, index, &DeviceInfoData);
2837
2838 if (!ok)
2839 {
2840 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2841 break;
2842 else
2843 {
2844 index++;
2845 continue;
2846 }
2847 }
2848
2849 /* try to get the hardware ID registry property */
2850 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2851 &DeviceInfoData,
2852 SPDRP_HARDWAREID,
2853 NULL,
2854 NULL,
2855 0,
2856 &size);
2857 if (!ok)
2858 {
2859 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2860 {
2861 index++;
2862 continue;
2863 }
2864
2865 deviceHwid = (TCHAR *) malloc (size);
2866 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2867 &DeviceInfoData,
2868 SPDRP_HARDWAREID,
2869 NULL,
2870 (PBYTE)deviceHwid,
2871 size,
2872 NULL);
2873 if (!ok)
2874 {
2875 free (deviceHwid);
2876 deviceHwid = NULL;
2877 index++;
2878 continue;
2879 }
2880 }
2881 else
2882 {
2883 /* something is wrong. This shouldn't have worked with a NULL buffer */
2884 index++;
2885 continue;
2886 }
2887
2888 for (TCHAR *t = deviceHwid;
2889 t && *t && t < &deviceHwid[size / sizeof(TCHAR)];
2890 t += _tcslen (t) + 1)
2891 {
2892 if (!_tcsicmp (DRIVERHWID, t))
2893 {
2894 /* get the device instance ID */
2895 TCHAR devID [MAX_DEVICE_ID_LEN];
2896 if (CM_Get_Device_ID(DeviceInfoData.DevInst,
2897 devID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2898 {
2899 /* compare to what we determined before */
2900 if (wcscmp(devID, lszPnPInstanceId) == 0)
2901 {
2902 found = TRUE;
2903 break;
2904 }
2905 }
2906 }
2907 }
2908
2909 if (deviceHwid)
2910 {
2911 free (deviceHwid);
2912 deviceHwid = NULL;
2913 }
2914
2915 if (found)
2916 break;
2917
2918 index++;
2919 }
2920
2921 if (found == FALSE)
2922 SetErrBreak ((tr ("Host Interface Network driver not found (0x%08X)"),
2923 GetLastError()));
2924
2925 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2926 if (!ok)
2927 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2928 GetLastError()));
2929
2930 ok = SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2931 if (!ok)
2932 SetErrBreak (("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)",
2933 GetLastError()));
2934 }
2935 while (0);
2936
2937 /* clean up the device info set */
2938 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2939 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2940
2941 if (VBOX_FAILURE (vrc))
2942 break;
2943 }
2944 while (0);
2945
2946 LogFlowFunc (("vrc=%Vrc\n", vrc));
2947 LogFlowFuncLeave();
2948 return vrc;
2949}
2950
2951#undef SetErrBreak
2952
2953/* static */
2954HRESULT Host::networkInterfaceHelperClient (SVCHlpClient *aClient,
2955 Progress *aProgress,
2956 void *aUser, int *aVrc)
2957{
2958 LogFlowFuncEnter();
2959 LogFlowFunc (("aClient={%p}, aProgress={%p}, aUser={%p}\n",
2960 aClient, aProgress, aUser));
2961
2962 AssertReturn ((aClient == NULL && aProgress == NULL && aVrc == NULL) ||
2963 (aClient != NULL && aProgress != NULL && aVrc != NULL),
2964 E_POINTER);
2965 AssertReturn (aUser, E_POINTER);
2966
2967 std::auto_ptr <NetworkInterfaceHelperClientData>
2968 d (static_cast <NetworkInterfaceHelperClientData *> (aUser));
2969
2970 if (aClient == NULL)
2971 {
2972 /* "cleanup only" mode, just return (it will free aUser) */
2973 return S_OK;
2974 }
2975
2976 HRESULT rc = S_OK;
2977 int vrc = VINF_SUCCESS;
2978
2979 switch (d->msgCode)
2980 {
2981 case SVCHlpMsg::CreateHostNetworkInterface:
2982 {
2983 LogFlowFunc (("CreateHostNetworkInterface:\n"));
2984 LogFlowFunc (("Network connection name = '%ls'\n", d->name.raw()));
2985
2986 /* write message and parameters */
2987 vrc = aClient->write (d->msgCode);
2988 if (VBOX_FAILURE (vrc)) break;
2989 vrc = aClient->write (Utf8Str (d->name));
2990 if (VBOX_FAILURE (vrc)) break;
2991
2992 /* wait for a reply */
2993 bool endLoop = false;
2994 while (!endLoop)
2995 {
2996 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
2997
2998 vrc = aClient->read (reply);
2999 if (VBOX_FAILURE (vrc)) break;
3000
3001 switch (reply)
3002 {
3003 case SVCHlpMsg::CreateHostNetworkInterface_OK:
3004 {
3005 /* read the GUID */
3006 Guid guid;
3007 vrc = aClient->read (guid);
3008 if (VBOX_FAILURE (vrc)) break;
3009
3010 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", guid.raw()));
3011
3012 /* initialize the object returned to the caller by
3013 * CreateHostNetworkInterface() */
3014 rc = d->iface->init (d->name, guid);
3015 endLoop = true;
3016 break;
3017 }
3018 case SVCHlpMsg::Error:
3019 {
3020 /* read the error message */
3021 Utf8Str errMsg;
3022 vrc = aClient->read (errMsg);
3023 if (VBOX_FAILURE (vrc)) break;
3024
3025 rc = setError (E_FAIL, errMsg);
3026 endLoop = true;
3027 break;
3028 }
3029 default:
3030 {
3031 endLoop = true;
3032 ComAssertMsgFailedBreak ((
3033 "Invalid message code %d (%08lX)\n",
3034 reply, reply),
3035 rc = E_FAIL);
3036 }
3037 }
3038 }
3039
3040 break;
3041 }
3042 case SVCHlpMsg::RemoveHostNetworkInterface:
3043 {
3044 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
3045 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", d->guid.raw()));
3046
3047 /* write message and parameters */
3048 vrc = aClient->write (d->msgCode);
3049 if (VBOX_FAILURE (vrc)) break;
3050 vrc = aClient->write (d->guid);
3051 if (VBOX_FAILURE (vrc)) break;
3052
3053 /* wait for a reply */
3054 bool endLoop = false;
3055 while (!endLoop)
3056 {
3057 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
3058
3059 vrc = aClient->read (reply);
3060 if (VBOX_FAILURE (vrc)) break;
3061
3062 switch (reply)
3063 {
3064 case SVCHlpMsg::OK:
3065 {
3066 /* no parameters */
3067 rc = S_OK;
3068 endLoop = true;
3069 break;
3070 }
3071 case SVCHlpMsg::Error:
3072 {
3073 /* read the error message */
3074 Utf8Str errMsg;
3075 vrc = aClient->read (errMsg);
3076 if (VBOX_FAILURE (vrc)) break;
3077
3078 rc = setError (E_FAIL, errMsg);
3079 endLoop = true;
3080 break;
3081 }
3082 default:
3083 {
3084 endLoop = true;
3085 ComAssertMsgFailedBreak ((
3086 "Invalid message code %d (%08lX)\n",
3087 reply, reply),
3088 rc = E_FAIL);
3089 }
3090 }
3091 }
3092
3093 break;
3094 }
3095 default:
3096 ComAssertMsgFailedBreak ((
3097 "Invalid message code %d (%08lX)\n",
3098 d->msgCode, d->msgCode),
3099 rc = E_FAIL);
3100 }
3101
3102 if (aVrc)
3103 *aVrc = vrc;
3104
3105 LogFlowFunc (("rc=0x%08X, vrc=%Vrc\n", rc, vrc));
3106 LogFlowFuncLeave();
3107 return rc;
3108}
3109
3110/* static */
3111int Host::networkInterfaceHelperServer (SVCHlpClient *aClient,
3112 SVCHlpMsg::Code aMsgCode)
3113{
3114 LogFlowFuncEnter();
3115 LogFlowFunc (("aClient={%p}, aMsgCode=%d\n", aClient, aMsgCode));
3116
3117 AssertReturn (aClient, VERR_INVALID_POINTER);
3118
3119 int vrc = VINF_SUCCESS;
3120
3121 switch (aMsgCode)
3122 {
3123 case SVCHlpMsg::CreateHostNetworkInterface:
3124 {
3125 LogFlowFunc (("CreateHostNetworkInterface:\n"));
3126
3127 Utf8Str name;
3128 vrc = aClient->read (name);
3129 if (VBOX_FAILURE (vrc)) break;
3130
3131 Guid guid;
3132 Utf8Str errMsg;
3133 vrc = createNetworkInterface (aClient, name, guid, errMsg);
3134
3135 if (VBOX_SUCCESS (vrc))
3136 {
3137 /* write success followed by GUID */
3138 vrc = aClient->write (SVCHlpMsg::CreateHostNetworkInterface_OK);
3139 if (VBOX_FAILURE (vrc)) break;
3140 vrc = aClient->write (guid);
3141 if (VBOX_FAILURE (vrc)) break;
3142 }
3143 else
3144 {
3145 /* write failure followed by error message */
3146 if (errMsg.isEmpty())
3147 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
3148 vrc = aClient->write (SVCHlpMsg::Error);
3149 if (VBOX_FAILURE (vrc)) break;
3150 vrc = aClient->write (errMsg);
3151 if (VBOX_FAILURE (vrc)) break;
3152 }
3153
3154 break;
3155 }
3156 case SVCHlpMsg::RemoveHostNetworkInterface:
3157 {
3158 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
3159
3160 Guid guid;
3161 vrc = aClient->read (guid);
3162 if (VBOX_FAILURE (vrc)) break;
3163
3164 Utf8Str errMsg;
3165 vrc = removeNetworkInterface (aClient, guid, errMsg);
3166
3167 if (VBOX_SUCCESS (vrc))
3168 {
3169 /* write parameter-less success */
3170 vrc = aClient->write (SVCHlpMsg::OK);
3171 if (VBOX_FAILURE (vrc)) break;
3172 }
3173 else
3174 {
3175 /* write failure followed by error message */
3176 if (errMsg.isEmpty())
3177 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
3178 vrc = aClient->write (SVCHlpMsg::Error);
3179 if (VBOX_FAILURE (vrc)) break;
3180 vrc = aClient->write (errMsg);
3181 if (VBOX_FAILURE (vrc)) break;
3182 }
3183
3184 break;
3185 }
3186 default:
3187 AssertMsgFailedBreak ((
3188 "Invalid message code %d (%08lX)\n", aMsgCode, aMsgCode),
3189 VERR_GENERAL_FAILURE);
3190 }
3191
3192 LogFlowFunc (("vrc=%Vrc\n", vrc));
3193 LogFlowFuncLeave();
3194 return vrc;
3195}
3196
3197#endif /* RT_OS_WINDOWS */
3198
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