VirtualBox

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

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

Added an maskedInterface property to the USB filters. It is used to hide a set of interface from the guest, which means that on Linux we won't capture those and linux can continue using them.

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