VirtualBox

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

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

Disabled host USB on solaris, not done yet.

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