VirtualBox

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

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

Attempt to fix libhal cdrom and floppy detection

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