VirtualBox

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

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

The Big Sun Rebranding Header Change

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