VirtualBox

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

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

Main: Reworked enums to avoid 1) weird duplication of enum name when referring to enum values in cross-platform code; 2) possible clashes on Win32 due to putting identifiers like Paused or Disabled to the global namespace (via C enums). In the new style, enums are used like this: a) USBDeviceState_T v = USBDeviceState_Busy from cross-platform non-Qt code; b) KUSBDeviceState v = KUSBDeviceState_Busy from Qt code; c) USBDeviceState v = USBDeviceState_Busy from plain Win32 and d) PRUInt32 USBDeviceState v = USBDeviceState::Busy from plain XPCOM.

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