VirtualBox

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

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

manual-cpp -DVBOX_WITH_USBFILTER. One worry, not sure why the Action setter call was omitted from the init. Hopefully just a left over.

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