VirtualBox

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

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

Solaris host USB: Initially proxy stuff.

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