VirtualBox

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

Last change on this file since 8455 was 8438, checked in by vboxsync, 17 years ago

Moved the requestCaptureForVM code.

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