VirtualBox

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

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

typo

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 109.8 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 = gLibHalFindDeviceByCapability(halContext,
1608 "storage.cdrom", &numDevices, &dbusError);
1609 if (halDevices != 0)
1610 {
1611 /* Hal is installed and working, so if no devices are reported, assume
1612 that there are none. */
1613 halSuccess = true;
1614 for (int i = 0; i < numDevices; i++)
1615 {
1616 char *devNode = gLibHalDeviceGetPropertyString(halContext,
1617 halDevices[i], "block.device", &dbusError);
1618#ifdef RT_OS_SOLARIS
1619 /* The CD/DVD ioctls work only for raw device nodes. */
1620 char *tmp = getfullrawname(devNode);
1621 gLibHalFreeString(devNode);
1622 devNode = tmp;
1623#endif
1624 if (devNode != 0)
1625 {
1626 if (validateDevice(devNode, true))
1627 {
1628 Utf8Str description;
1629 char *vendor, *product;
1630 /* We do not check the error here, as this field may
1631 not even exist. */
1632 vendor = gLibHalDeviceGetPropertyString(halContext,
1633 halDevices[i], "info.vendor", 0);
1634 product = gLibHalDeviceGetPropertyString(halContext,
1635 halDevices[i], "info.product", &dbusError);
1636 if ((product != 0 && product[0] != 0))
1637 {
1638 if ((vendor != 0) && (vendor[0] != 0))
1639 {
1640 description = Utf8StrFmt ("%s %s",
1641 vendor, product);
1642 }
1643 else
1644 {
1645 description = product;
1646 }
1647 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1648 hostDVDDriveObj.createObject();
1649 hostDVDDriveObj->init (Bstr (devNode),
1650 Bstr (halDevices[i]),
1651 Bstr (description));
1652 list.push_back (hostDVDDriveObj);
1653 }
1654 else
1655 {
1656 if (product == 0)
1657 {
1658 LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
1659 halDevices[i], dbusError.name, dbusError.message));
1660 gDBusErrorFree(&dbusError);
1661 }
1662 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1663 hostDVDDriveObj.createObject();
1664 hostDVDDriveObj->init (Bstr (devNode),
1665 Bstr (halDevices[i]));
1666 list.push_back (hostDVDDriveObj);
1667 }
1668 if (vendor != 0)
1669 {
1670 gLibHalFreeString(vendor);
1671 }
1672 if (product != 0)
1673 {
1674 gLibHalFreeString(product);
1675 }
1676 }
1677 else
1678 {
1679 LogRel(("Host::COMGETTER(DVDDrives): failed to validate the block device %s as a DVD drive\n"));
1680 }
1681#ifndef RT_OS_SOLARIS
1682 gLibHalFreeString(devNode);
1683#else
1684 free(devNode);
1685#endif
1686 }
1687 else
1688 {
1689 LogRel(("Host::COMGETTER(DVDDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
1690 halDevices[i], dbusError.name, dbusError.message));
1691 gDBusErrorFree(&dbusError);
1692 }
1693 }
1694 gLibHalFreeStringArray(halDevices);
1695 }
1696 else
1697 {
1698 LogRel(("Host::COMGETTER(DVDDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1699 gDBusErrorFree(&dbusError);
1700 }
1701 if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */
1702 {
1703 LogRel(("Host::COMGETTER(DVDDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1704 gDBusErrorFree(&dbusError);
1705 }
1706 }
1707 else
1708 {
1709 LogRel(("Host::COMGETTER(DVDDrives): failed to initialise libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1710 gDBusErrorFree(&dbusError);
1711 }
1712 gLibHalCtxFree(halContext);
1713 }
1714 else
1715 {
1716 LogRel(("Host::COMGETTER(DVDDrives): failed to set libhal connection to dbus.\n"));
1717 }
1718 }
1719 else
1720 {
1721 LogRel(("Host::COMGETTER(DVDDrives): failed to get a libhal context - out of memory?\n"));
1722 }
1723 gDBusConnectionUnref(dbusConnection);
1724 }
1725 else
1726 {
1727 LogRel(("Host::COMGETTER(DVDDrives): failed to connect to dbus. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1728 gDBusErrorFree(&dbusError);
1729 }
1730 return halSuccess;
1731}
1732
1733
1734/**
1735 * Helper function to query the hal subsystem for information about floppy drives attached to the
1736 * system.
1737 *
1738 * @returns true if information was successfully obtained, false otherwise
1739 * @retval list drives found will be attached to this list
1740 */
1741bool Host::getFloppyInfoFromHal(std::list <ComObjPtr <HostFloppyDrive> > &list)
1742{
1743 bool halSuccess = false;
1744 DBusError dbusError;
1745 if (!gLibHalCheckPresence())
1746 return false;
1747 gDBusErrorInit (&dbusError);
1748 DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError);
1749 if (dbusConnection != 0)
1750 {
1751 LibHalContext *halContext = gLibHalCtxNew();
1752 if (halContext != 0)
1753 {
1754 if (gLibHalCtxSetDBusConnection (halContext, dbusConnection))
1755 {
1756 if (gLibHalCtxInit(halContext, &dbusError))
1757 {
1758 int numDevices;
1759 char **halDevices = gLibHalFindDeviceByCapability(halContext,
1760 "storage", &numDevices, &dbusError);
1761 if (halDevices != 0)
1762 {
1763 /* Hal is installed and working, so if no devices are reported, assume
1764 that there are none. */
1765 halSuccess = true;
1766 for (int i = 0; i < numDevices; i++)
1767 {
1768 char *driveType = gLibHalDeviceGetPropertyString(halContext,
1769 halDevices[i], "storage.drive_type", 0);
1770 if (driveType != 0)
1771 {
1772 if (strcmp(driveType, "floppy") != 0)
1773 {
1774 gLibHalFreeString(driveType);
1775 continue;
1776 }
1777 gLibHalFreeString(driveType);
1778 }
1779 else
1780 {
1781 /* An error occurred. The attribute "storage.drive_type"
1782 probably didn't exist. */
1783 continue;
1784 }
1785 char *devNode = gLibHalDeviceGetPropertyString(halContext,
1786 halDevices[i], "block.device", &dbusError);
1787 if (devNode != 0)
1788 {
1789 if (validateDevice(devNode, false))
1790 {
1791 Utf8Str description;
1792 char *vendor, *product;
1793 /* We do not check the error here, as this field may
1794 not even exist. */
1795 vendor = gLibHalDeviceGetPropertyString(halContext,
1796 halDevices[i], "info.vendor", 0);
1797 product = gLibHalDeviceGetPropertyString(halContext,
1798 halDevices[i], "info.product", &dbusError);
1799 if ((product != 0) && (product[0] != 0))
1800 {
1801 if ((vendor != 0) && (vendor[0] != 0))
1802 {
1803 description = Utf8StrFmt ("%s %s",
1804 vendor, product);
1805 }
1806 else
1807 {
1808 description = product;
1809 }
1810 ComObjPtr <HostFloppyDrive> hostFloppyDrive;
1811 hostFloppyDrive.createObject();
1812 hostFloppyDrive->init (Bstr (devNode),
1813 Bstr (halDevices[i]),
1814 Bstr (description));
1815 list.push_back (hostFloppyDrive);
1816 }
1817 else
1818 {
1819 if (product == 0)
1820 {
1821 LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"info.product\" for device %s. dbus error: %s (%s)\n",
1822 halDevices[i], dbusError.name, dbusError.message));
1823 gDBusErrorFree(&dbusError);
1824 }
1825 ComObjPtr <HostFloppyDrive> hostFloppyDrive;
1826 hostFloppyDrive.createObject();
1827 hostFloppyDrive->init (Bstr (devNode),
1828 Bstr (halDevices[i]));
1829 list.push_back (hostFloppyDrive);
1830 }
1831 if (vendor != 0)
1832 {
1833 gLibHalFreeString(vendor);
1834 }
1835 if (product != 0)
1836 {
1837 gLibHalFreeString(product);
1838 }
1839 }
1840 else
1841 {
1842 LogRel(("Host::COMGETTER(FloppyDrives): failed to validate the block device %s as a floppy drive\n"));
1843 }
1844 gLibHalFreeString(devNode);
1845 }
1846 else
1847 {
1848 LogRel(("Host::COMGETTER(FloppyDrives): failed to get property \"block.device\" for device %s. dbus error: %s (%s)\n",
1849 halDevices[i], dbusError.name, dbusError.message));
1850 gDBusErrorFree(&dbusError);
1851 }
1852 }
1853 gLibHalFreeStringArray(halDevices);
1854 }
1855 else
1856 {
1857 LogRel(("Host::COMGETTER(FloppyDrives): failed to get devices with capability \"storage.cdrom\". dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1858 gDBusErrorFree(&dbusError);
1859 }
1860 if (!gLibHalCtxShutdown(halContext, &dbusError)) /* what now? */
1861 {
1862 LogRel(("Host::COMGETTER(FloppyDrives): failed to shutdown the libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1863 gDBusErrorFree(&dbusError);
1864 }
1865 }
1866 else
1867 {
1868 LogRel(("Host::COMGETTER(FloppyDrives): failed to initialise libhal context. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1869 gDBusErrorFree(&dbusError);
1870 }
1871 gLibHalCtxFree(halContext);
1872 }
1873 else
1874 {
1875 LogRel(("Host::COMGETTER(FloppyDrives): failed to set libhal connection to dbus.\n"));
1876 }
1877 }
1878 else
1879 {
1880 LogRel(("Host::COMGETTER(FloppyDrives): failed to get a libhal context - out of memory?\n"));
1881 }
1882 gDBusConnectionUnref(dbusConnection);
1883 }
1884 else
1885 {
1886 LogRel(("Host::COMGETTER(FloppyDrives): failed to connect to dbus. dbus error: %s (%s)\n", dbusError.name, dbusError.message));
1887 gDBusErrorFree(&dbusError);
1888 }
1889 return halSuccess;
1890}
1891# endif /* VBOX_USE_HAL defined */
1892
1893/**
1894 * Helper function to parse the given mount file and add found entries
1895 */
1896void Host::parseMountTable(char *mountTable, std::list <ComObjPtr <HostDVDDrive> > &list)
1897{
1898#ifdef RT_OS_LINUX
1899 FILE *mtab = setmntent(mountTable, "r");
1900 if (mtab)
1901 {
1902 struct mntent *mntent;
1903 char *mnt_type;
1904 char *mnt_dev;
1905 char *tmp;
1906 while ((mntent = getmntent(mtab)))
1907 {
1908 mnt_type = (char*)malloc(strlen(mntent->mnt_type) + 1);
1909 mnt_dev = (char*)malloc(strlen(mntent->mnt_fsname) + 1);
1910 strcpy(mnt_type, mntent->mnt_type);
1911 strcpy(mnt_dev, mntent->mnt_fsname);
1912 // supermount fs case
1913 if (strcmp(mnt_type, "supermount") == 0)
1914 {
1915 tmp = strstr(mntent->mnt_opts, "fs=");
1916 if (tmp)
1917 {
1918 free(mnt_type);
1919 mnt_type = strdup(tmp + strlen("fs="));
1920 if (mnt_type)
1921 {
1922 tmp = strchr(mnt_type, ',');
1923 if (tmp)
1924 *tmp = '\0';
1925 }
1926 }
1927 tmp = strstr(mntent->mnt_opts, "dev=");
1928 if (tmp)
1929 {
1930 free(mnt_dev);
1931 mnt_dev = strdup(tmp + strlen("dev="));
1932 if (mnt_dev)
1933 {
1934 tmp = strchr(mnt_dev, ',');
1935 if (tmp)
1936 *tmp = '\0';
1937 }
1938 }
1939 }
1940 // use strstr here to cover things fs types like "udf,iso9660"
1941 if (strstr(mnt_type, "iso9660") == 0)
1942 {
1943 /** @todo check whether we've already got the drive in our list! */
1944 if (validateDevice(mnt_dev, true))
1945 {
1946 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1947 hostDVDDriveObj.createObject();
1948 hostDVDDriveObj->init (Bstr (mnt_dev));
1949 list.push_back (hostDVDDriveObj);
1950 }
1951 }
1952 free(mnt_dev);
1953 free(mnt_type);
1954 }
1955 endmntent(mtab);
1956 }
1957#else // RT_OS_SOLARIS
1958 FILE *mntFile = fopen(mountTable, "r");
1959 if (mntFile)
1960 {
1961 struct mnttab mntTab;
1962 while (getmntent(mntFile, &mntTab) == 0)
1963 {
1964 char *mountName = strdup(mntTab.mnt_special);
1965 char *mountPoint = strdup(mntTab.mnt_mountp);
1966 char *mountFSType = strdup(mntTab.mnt_fstype);
1967
1968 // skip devices we are not interested in
1969 if ((*mountName && mountName[0] == '/') && // skip 'fake' devices (like -hosts, proc, fd, swap)
1970 (*mountFSType && (strcmp(mountFSType, "devfs") != 0 && // skip devfs (i.e. /devices)
1971 strcmp(mountFSType, "dev") != 0 && // skip dev (i.e. /dev)
1972 strcmp(mountFSType, "lofs") != 0)) && // skip loop-back file-system (lofs)
1973 (*mountPoint && strcmp(mountPoint, "/") != 0)) // skip point '/' (Can CD/DVD be mounted at '/' ???)
1974 {
1975 char *rawDevName = getfullrawname(mountName);
1976 if (validateDevice(rawDevName, true))
1977 {
1978 ComObjPtr <HostDVDDrive> hostDVDDriveObj;
1979 hostDVDDriveObj.createObject();
1980 hostDVDDriveObj->init (Bstr (rawDevName));
1981 list.push_back (hostDVDDriveObj);
1982 }
1983 free(rawDevName);
1984 }
1985
1986 free(mountName);
1987 free(mountPoint);
1988 free(mountFSType);
1989 }
1990
1991 fclose(mntFile);
1992 }
1993#endif
1994}
1995
1996/**
1997 * Helper function to check whether the given device node is a valid drive
1998 */
1999bool Host::validateDevice(const char *deviceNode, bool isCDROM)
2000{
2001 struct stat statInfo;
2002 bool retValue = false;
2003
2004 // sanity check
2005 if (!deviceNode)
2006 {
2007 return false;
2008 }
2009
2010 // first a simple stat() call
2011 if (stat(deviceNode, &statInfo) < 0)
2012 {
2013 return false;
2014 } else
2015 {
2016 if (isCDROM)
2017 {
2018 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
2019 {
2020 int fileHandle;
2021 // now try to open the device
2022 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
2023 if (fileHandle >= 0)
2024 {
2025 cdrom_subchnl cdChannelInfo;
2026 cdChannelInfo.cdsc_format = CDROM_MSF;
2027 // this call will finally reveal the whole truth
2028#ifdef RT_OS_LINUX
2029 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
2030 (errno == EIO) || (errno == ENOENT) ||
2031 (errno == EINVAL) || (errno == ENOMEDIUM))
2032#else
2033 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
2034 (errno == EIO) || (errno == ENOENT) ||
2035 (errno == EINVAL))
2036#endif
2037 {
2038 retValue = true;
2039 }
2040 close(fileHandle);
2041 }
2042 }
2043 } else
2044 {
2045 // floppy case
2046 if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
2047 {
2048 /// @todo do some more testing, maybe a nice IOCTL!
2049 retValue = true;
2050 }
2051 }
2052 }
2053 return retValue;
2054}
2055#endif // RT_OS_LINUX || RT_OS_SOLARIS
2056
2057/**
2058 * Applies all (golbal and VM) filters to the given USB device. The device
2059 * must be either a newly attached device or a device released by a VM.
2060 *
2061 * This method will request the USB proxy service to release the device (give
2062 * it back to the host) if none of the global or VM filters want to capture
2063 * the device.
2064 *
2065 * @param aDevice USB device to apply filters to.
2066 * @param aMachine Machine the device was released by or @c NULL.
2067 *
2068 * @note the method must be called from under this object's write lock and
2069 * from the aDevice's write lock.
2070 */
2071HRESULT Host::applyAllUSBFilters (ComObjPtr <HostUSBDevice> &aDevice,
2072 SessionMachine *aMachine /* = NULL */)
2073{
2074 LogFlowThisFunc (("\n"));
2075
2076 /// @todo must check for read lock, it's enough here
2077 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
2078
2079 AssertReturn (aDevice->isLockedOnCurrentThread(), E_FAIL);
2080
2081 AssertReturn (aDevice->state() != USBDeviceState_USBDeviceCaptured, E_FAIL);
2082
2083 AssertReturn (aDevice->isStatePending() == false, E_FAIL);
2084
2085 /* ignore unsupported devices */
2086 if (aDevice->state() == USBDeviceState_USBDeviceNotSupported)
2087 return S_OK;
2088 /* ignore unavailable devices as well */
2089 if (aDevice->state() == USBDeviceState_USBDeviceUnavailable)
2090 return S_OK;
2091
2092 VirtualBox::SessionMachineVector machines;
2093 mParent->getOpenedMachines (machines);
2094
2095 /// @todo it may be better to take a copy of filters to iterate and leave
2096 /// the host lock before calling HostUSBDevice:requestCapture() (which
2097 /// calls the VM process).
2098
2099 /* apply global filters */
2100 USBDeviceFilterList::const_iterator it = mUSBDeviceFilters.begin();
2101 for (; it != mUSBDeviceFilters.end(); ++ it)
2102 {
2103 AutoLock filterLock (*it);
2104 const HostUSBDeviceFilter::Data &data = (*it)->data();
2105 if (aDevice->isMatch (data))
2106 {
2107#ifndef VBOX_WITH_USBFILTER
2108 USBDeviceFilterAction_T action = data.mAction;
2109#else
2110 ULONG action = USBDeviceFilterAction_InvalidUSBDeviceFilterAction;
2111 (*it)->COMGETTER (Action) (&action);
2112#endif
2113 if (action == USBDeviceFilterAction_USBDeviceFilterIgnore)
2114 {
2115 /* request to give the device back to the host*/
2116 aDevice->requestRelease();
2117 /* nothing to do any more */
2118 return S_OK;
2119 }
2120 if (action == USBDeviceFilterAction_USBDeviceFilterHold)
2121 break;
2122 }
2123 }
2124
2125 /* apply machine filters */
2126 size_t i = 0;
2127 for (; i < machines.size(); ++ i)
2128 {
2129 /* skip the machine the device was just detached from */
2130 if (aMachine && machines [i] == aMachine)
2131 continue;
2132
2133 if (applyMachineUSBFilters (machines [i], aDevice))
2134 break;
2135 }
2136
2137 if (i == machines.size())
2138 {
2139 /* no matched machine filters, check what to do */
2140 if (it == mUSBDeviceFilters.end())
2141 {
2142 /* no any filter matched at all */
2143 /* request to give the device back to the host */
2144 aDevice->requestRelease();
2145 }
2146 else
2147 {
2148 /* there was a global Hold filter */
2149 aDevice->requestHold();
2150 }
2151 }
2152
2153 return S_OK;
2154}
2155
2156/**
2157 * Runs through filters of the given machine and asks the USB proxy service
2158 * to capture the given USB device when there is a match.
2159 *
2160 * @param aMachine Machine whose filters are to be run.
2161 * @param aDevice USB device, a candidate for auto-capturing.
2162 * @return @c true if there was a match and @c false otherwise.
2163 *
2164 * @note the method must be called from under this object's write lock and
2165 * from the aDevice's write lock.
2166 *
2167 * @note Locks aMachine for reading.
2168 */
2169bool Host::applyMachineUSBFilters (SessionMachine *aMachine,
2170 ComObjPtr <HostUSBDevice> &aDevice)
2171{
2172 LogFlowThisFunc (("\n"));
2173
2174 AssertReturn (aMachine, false);
2175
2176 /// @todo must check for read lock, it's enough here
2177 AssertReturn (isLockedOnCurrentThread(), false);
2178
2179 AssertReturn (aDevice->isLockedOnCurrentThread(), false);
2180
2181 AssertReturn (aDevice->state() != USBDeviceState_USBDeviceNotSupported, false);
2182 AssertReturn (aDevice->state() != USBDeviceState_USBDeviceUnavailable, false);
2183
2184 AssertReturn (aDevice->isStatePending() == false, false);
2185
2186 bool hasMatch = false;
2187
2188 {
2189 /* We're going to use aMachine which is not our child/parent, add a
2190 * caller */
2191 AutoCaller autoCaller (aMachine);
2192 if (!autoCaller.isOk())
2193 {
2194 /* silently return, the machine might be not running any more */
2195 return false;
2196 }
2197
2198 /* enter the machine's lock because we want to access its USB controller */
2199 AutoReaderLock machineLock (aMachine);
2200 hasMatch = aMachine->usbController()->hasMatchingFilter (aDevice);
2201 }
2202
2203 if (hasMatch)
2204 {
2205 /* try to capture the device */
2206 return aDevice->requestCapture (aMachine);
2207 }
2208
2209 return hasMatch;
2210}
2211
2212/**
2213 * Called by USB proxy service when a new device is physically attached
2214 * to the host.
2215 *
2216 * @param aDevice Pointer to the device which has been attached.
2217 */
2218void Host::onUSBDeviceAttached (HostUSBDevice *aDevice)
2219{
2220 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2221
2222 AssertReturnVoid (aDevice);
2223
2224 AssertReturnVoid (isLockedOnCurrentThread());
2225 AssertReturnVoid (aDevice->isLockedOnCurrentThread());
2226
2227 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2228 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2229 aDevice->pendingState()));
2230
2231 Assert (aDevice->isStatePending() == false);
2232
2233 /* add to the collecion */
2234 mUSBDevices.push_back (aDevice);
2235
2236 /* apply all filters */
2237 ComObjPtr <HostUSBDevice> device (aDevice);
2238 HRESULT rc = applyAllUSBFilters (device);
2239 AssertComRC (rc);
2240}
2241
2242/**
2243 * Called by USB proxy service when the device is physically detached
2244 * from the host.
2245 *
2246 * @param aDevice Pointer to the device which has been detached.
2247 */
2248void Host::onUSBDeviceDetached (HostUSBDevice *aDevice)
2249{
2250 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2251
2252 AssertReturnVoid (aDevice);
2253
2254 AssertReturnVoid (isLockedOnCurrentThread());
2255 AssertReturnVoid (aDevice->isLockedOnCurrentThread());
2256
2257 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2258 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2259 aDevice->pendingState()));
2260
2261 Guid id = aDevice->id();
2262
2263 ComObjPtr <HostUSBDevice> device;
2264 Host::USBDeviceList::iterator it = mUSBDevices.begin();
2265 while (it != mUSBDevices.end())
2266 {
2267 if ((*it)->id() == id)
2268 {
2269 device = (*it);
2270 break;
2271 }
2272 ++ it;
2273 }
2274
2275 AssertReturnVoid (!!device);
2276
2277 /* remove from the collecion */
2278 mUSBDevices.erase (it);
2279
2280 /* Detach the device from any machine currently using it,
2281 reset all data and uninitialize the device object. */
2282 device->onDetachedPhys();
2283}
2284
2285/**
2286 * Called by USB proxy service when the state of the device has changed
2287 * either because of the state change request or because of some external
2288 * interaction.
2289 *
2290 * @param aDevice The device in question.
2291 */
2292void Host::onUSBDeviceStateChanged (HostUSBDevice *aDevice)
2293{
2294 LogFlowThisFunc (("aDevice=%p\n", aDevice));
2295
2296 AssertReturnVoid (aDevice);
2297
2298 AssertReturnVoid (isLockedOnCurrentThread());
2299 AssertReturnVoid (aDevice->isLockedOnCurrentThread());
2300
2301 LogFlowThisFunc (("id={%Vuuid} state=%d isStatePending=%RTbool pendingState=%d\n",
2302 aDevice->id().raw(), aDevice->state(), aDevice->isStatePending(),
2303 aDevice->pendingState()));
2304
2305
2306 ComObjPtr <HostUSBDevice> device (aDevice);
2307 if (device->isStatePending())
2308 {
2309 /* it was a state change request */
2310 if (device->pendingStateEx() == HostUSBDevice::kDetachingPendingAttachFilters)
2311 {
2312 /* The device has completed an asynchronous detach operation, subject
2313 it to the filters and such if the current state permits this.
2314 (handlePendingStateChange will disassociate itself from the machine.) */
2315 ComObjPtr <SessionMachine> machine (device->machine());
2316 device->handlePendingStateChange();
2317 if (device->state() == USBDeviceState_USBDeviceCaptured)
2318 {
2319 Log (("USB: running filters on async detached device\n"));
2320 device->setHeld();
2321 HRESULT rc = applyAllUSBFilters (device, machine);
2322 AssertComRC (rc);
2323 }
2324 else
2325 Log (("USB: async detached devices reappeared in stated %d instead of %d!\n",
2326 device->state(), USBDeviceState_USBDeviceCaptured));
2327 }
2328 else
2329 device->handlePendingStateChange();
2330 }
2331 else if ( device->state() == USBDeviceState_USBDeviceAvailable
2332 || device->state() == USBDeviceState_USBDeviceBusy)
2333 {
2334 /* The device has gone from being unavailable (not subject to filters) to being
2335 available / busy. This transition can be triggered by udevd or manual
2336 permission changes on Linux. On all systems may be triggered by the host
2337 ceasing to use the device - like unmounting an MSD in the Finder or invoking
2338 the "Safely remove XXXX" stuff on Windows (perhaps). */
2339 HRESULT rc = applyAllUSBFilters (device);
2340 AssertComRC (rc);
2341 }
2342 else
2343 {
2344 /* some external state change */
2345
2346 /// @todo re-run all USB filters probably
2347 AssertFailed();
2348 }
2349}
2350
2351/**
2352 * Checks for the presense and status of the USB Proxy Service.
2353 * Returns S_OK when the Proxy is present and OK, or E_FAIL and a
2354 * corresponding error message otherwise. Intended to be used by methods
2355 * that rely on the Proxy Service availability.
2356 *
2357 * @note Locks this object for reading.
2358 */
2359HRESULT Host::checkUSBProxyService()
2360{
2361#ifdef VBOX_WITH_USB
2362 AutoLock lock (this);
2363 CHECK_READY();
2364
2365 AssertReturn (mUSBProxyService, E_FAIL);
2366 if (!mUSBProxyService->isActive())
2367 {
2368 /* disable the USB controller completely to avoid assertions if the
2369 * USB proxy service could not start. */
2370
2371 Assert (VBOX_FAILURE (mUSBProxyService->getLastError()));
2372 if (mUSBProxyService->getLastError() == VERR_FILE_NOT_FOUND)
2373 return setError (E_FAIL,
2374 tr ("Could not load the Host USB Proxy Service (%Vrc). "
2375 "The service might be not installed on the host computer"),
2376 mUSBProxyService->getLastError());
2377 else
2378 return setError (E_FAIL,
2379 tr ("Could not load the Host USB Proxy service (%Vrc)"),
2380 mUSBProxyService->getLastError());
2381 }
2382
2383 return S_OK;
2384#else
2385 return E_NOTIMPL;
2386#endif
2387}
2388
2389#ifdef RT_OS_WINDOWS
2390
2391/* The original source of the VBoxTAP adapter creation/destruction code has the following copyright */
2392/*
2393 Copyright 2004 by the Massachusetts Institute of Technology
2394
2395 All rights reserved.
2396
2397 Permission to use, copy, modify, and distribute this software and its
2398 documentation for any purpose and without fee is hereby granted,
2399 provided that the above copyright notice appear in all copies and that
2400 both that copyright notice and this permission notice appear in
2401 supporting documentation, and that the name of the Massachusetts
2402 Institute of Technology (M.I.T.) not be used in advertising or publicity
2403 pertaining to distribution of the software without specific, written
2404 prior permission.
2405
2406 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
2407 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
2408 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
2409 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
2410 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2411 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2412 SOFTWARE.
2413*/
2414
2415
2416#define NETSHELL_LIBRARY _T("netshell.dll")
2417
2418/**
2419 * Use the IShellFolder API to rename the connection.
2420 */
2421static HRESULT rename_shellfolder (PCWSTR wGuid, PCWSTR wNewName)
2422{
2423 /* This is the GUID for the network connections folder. It is constant.
2424 * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */
2425 const GUID CLSID_NetworkConnections = {
2426 0x7007ACC7, 0x3202, 0x11D1, {
2427 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
2428 }
2429 };
2430
2431 LPITEMIDLIST pidl = NULL;
2432 IShellFolder *pShellFolder = NULL;
2433 HRESULT hr;
2434
2435 /* Build the display name in the form "::{GUID}". */
2436 if (wcslen (wGuid) >= MAX_PATH)
2437 return E_INVALIDARG;
2438 WCHAR szAdapterGuid[MAX_PATH + 2] = {0};
2439 swprintf (szAdapterGuid, L"::%ls", wGuid);
2440
2441 /* Create an instance of the network connections folder. */
2442 hr = CoCreateInstance (CLSID_NetworkConnections, NULL,
2443 CLSCTX_INPROC_SERVER, IID_IShellFolder,
2444 reinterpret_cast <LPVOID *> (&pShellFolder));
2445 /* Parse the display name. */
2446 if (SUCCEEDED (hr))
2447 {
2448 hr = pShellFolder->ParseDisplayName (NULL, NULL, szAdapterGuid, NULL,
2449 &pidl, NULL);
2450 }
2451 if (SUCCEEDED (hr))
2452 {
2453 hr = pShellFolder->SetNameOf (NULL, pidl, wNewName, SHGDN_NORMAL,
2454 &pidl);
2455 }
2456
2457 CoTaskMemFree (pidl);
2458
2459 if (pShellFolder)
2460 pShellFolder->Release();
2461
2462 return hr;
2463}
2464
2465extern "C" HRESULT RenameConnection (PCWSTR GuidString, PCWSTR NewName)
2466{
2467 typedef HRESULT (WINAPI *lpHrRenameConnection) (const GUID *, PCWSTR);
2468 lpHrRenameConnection RenameConnectionFunc = NULL;
2469 HRESULT status;
2470
2471 /* First try the IShellFolder interface, which was unimplemented
2472 * for the network connections folder before XP. */
2473 status = rename_shellfolder (GuidString, NewName);
2474 if (status == E_NOTIMPL)
2475 {
2476/** @todo that code doesn't seem to work! */
2477 /* The IShellFolder interface is not implemented on this platform.
2478 * Try the (undocumented) HrRenameConnection API in the netshell
2479 * library. */
2480 CLSID clsid;
2481 HINSTANCE hNetShell;
2482 status = CLSIDFromString ((LPOLESTR) GuidString, &clsid);
2483 if (FAILED(status))
2484 return E_FAIL;
2485 hNetShell = LoadLibrary (NETSHELL_LIBRARY);
2486 if (hNetShell == NULL)
2487 return E_FAIL;
2488 RenameConnectionFunc =
2489 (lpHrRenameConnection) GetProcAddress (hNetShell,
2490 "HrRenameConnection");
2491 if (RenameConnectionFunc == NULL)
2492 {
2493 FreeLibrary (hNetShell);
2494 return E_FAIL;
2495 }
2496 status = RenameConnectionFunc (&clsid, NewName);
2497 FreeLibrary (hNetShell);
2498 }
2499 if (FAILED (status))
2500 return status;
2501
2502 return S_OK;
2503}
2504
2505#define DRIVERHWID _T("vboxtap")
2506
2507#define SetErrBreak(strAndArgs) \
2508 if (1) { \
2509 aErrMsg = Utf8StrFmt strAndArgs; vrc = VERR_GENERAL_FAILURE; break; \
2510 } else do {} while (0)
2511
2512/* static */
2513int Host::createNetworkInterface (SVCHlpClient *aClient,
2514 const Utf8Str &aName,
2515 Guid &aGUID, Utf8Str &aErrMsg)
2516{
2517 LogFlowFuncEnter();
2518 LogFlowFunc (("Network connection name = '%s'\n", aName.raw()));
2519
2520 AssertReturn (aClient, VERR_INVALID_POINTER);
2521 AssertReturn (!aName.isNull(), VERR_INVALID_PARAMETER);
2522
2523 int vrc = VINF_SUCCESS;
2524
2525 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2526 SP_DEVINFO_DATA DeviceInfoData;
2527 DWORD ret = 0;
2528 BOOL found = FALSE;
2529 BOOL registered = FALSE;
2530 BOOL destroyList = FALSE;
2531 TCHAR pCfgGuidString [50];
2532
2533 do
2534 {
2535 BOOL ok;
2536 GUID netGuid;
2537 SP_DRVINFO_DATA DriverInfoData;
2538 SP_DEVINSTALL_PARAMS DeviceInstallParams;
2539 TCHAR className [MAX_PATH];
2540 DWORD index = 0;
2541 PSP_DRVINFO_DETAIL_DATA pDriverInfoDetail;
2542 /* for our purposes, 2k buffer is more
2543 * than enough to obtain the hardware ID
2544 * of the VBoxTAP driver. */
2545 DWORD detailBuf [2048];
2546
2547 HKEY hkey = NULL;
2548 DWORD cbSize;
2549 DWORD dwValueType;
2550
2551 /* initialize the structure size */
2552 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
2553 DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
2554
2555 /* copy the net class GUID */
2556 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
2557
2558 /* create an empty device info set associated with the net class GUID */
2559 hDeviceInfo = SetupDiCreateDeviceInfoList (&netGuid, NULL);
2560 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2561 SetErrBreak (("SetupDiCreateDeviceInfoList failed (0x%08X)",
2562 GetLastError()));
2563
2564 /* get the class name from GUID */
2565 ok = SetupDiClassNameFromGuid (&netGuid, className, MAX_PATH, NULL);
2566 if (!ok)
2567 SetErrBreak (("SetupDiClassNameFromGuid failed (0x%08X)",
2568 GetLastError()));
2569
2570 /* create a device info element and add the new device instance
2571 * key to registry */
2572 ok = SetupDiCreateDeviceInfo (hDeviceInfo, className, &netGuid, NULL, NULL,
2573 DICD_GENERATE_ID, &DeviceInfoData);
2574 if (!ok)
2575 SetErrBreak (("SetupDiCreateDeviceInfo failed (0x%08X)",
2576 GetLastError()));
2577
2578 /* select the newly created device info to be the currently
2579 selected member */
2580 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2581 if (!ok)
2582 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2583 GetLastError()));
2584
2585 /* build a list of class drivers */
2586 ok = SetupDiBuildDriverInfoList (hDeviceInfo, &DeviceInfoData,
2587 SPDIT_CLASSDRIVER);
2588 if (!ok)
2589 SetErrBreak (("SetupDiBuildDriverInfoList failed (0x%08X)",
2590 GetLastError()));
2591
2592 destroyList = TRUE;
2593
2594 /* enumerate the driver info list */
2595 while (TRUE)
2596 {
2597 BOOL ret;
2598
2599 ret = SetupDiEnumDriverInfo (hDeviceInfo, &DeviceInfoData,
2600 SPDIT_CLASSDRIVER, index, &DriverInfoData);
2601
2602 /* if the function failed and GetLastError() returned
2603 * ERROR_NO_MORE_ITEMS, then we have reached the end of the
2604 * list. Othewise there was something wrong with this
2605 * particular driver. */
2606 if (!ret)
2607 {
2608 if(GetLastError() == ERROR_NO_MORE_ITEMS)
2609 break;
2610 else
2611 {
2612 index++;
2613 continue;
2614 }
2615 }
2616
2617 pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA) detailBuf;
2618 pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
2619
2620 /* if we successfully find the hardware ID and it turns out to
2621 * be the one for the loopback driver, then we are done. */
2622 if (SetupDiGetDriverInfoDetail (hDeviceInfo,
2623 &DeviceInfoData,
2624 &DriverInfoData,
2625 pDriverInfoDetail,
2626 sizeof (detailBuf),
2627 NULL))
2628 {
2629 TCHAR * t;
2630
2631 /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the
2632 * whole list and see if there is a match somewhere. */
2633 t = pDriverInfoDetail->HardwareID;
2634 while (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2635 {
2636 if (!_tcsicmp(t, DRIVERHWID))
2637 break;
2638
2639 t += _tcslen(t) + 1;
2640 }
2641
2642 if (t && *t && t < (TCHAR *) &detailBuf [sizeof(detailBuf) / sizeof (detailBuf[0])])
2643 {
2644 found = TRUE;
2645 break;
2646 }
2647 }
2648
2649 index ++;
2650 }
2651
2652 if (!found)
2653 SetErrBreak ((tr ("Could not find Host Interface Networking driver! "
2654 "Please reinstall")));
2655
2656 /* set the loopback driver to be the currently selected */
2657 ok = SetupDiSetSelectedDriver (hDeviceInfo, &DeviceInfoData,
2658 &DriverInfoData);
2659 if (!ok)
2660 SetErrBreak (("SetupDiSetSelectedDriver failed (0x%08X)",
2661 GetLastError()));
2662
2663 /* register the phantom device to prepare for install */
2664 ok = SetupDiCallClassInstaller (DIF_REGISTERDEVICE, hDeviceInfo,
2665 &DeviceInfoData);
2666 if (!ok)
2667 SetErrBreak (("SetupDiCallClassInstaller failed (0x%08X)",
2668 GetLastError()));
2669
2670 /* registered, but remove if errors occur in the following code */
2671 registered = TRUE;
2672
2673 /* ask the installer if we can install the device */
2674 ok = SetupDiCallClassInstaller (DIF_ALLOW_INSTALL, hDeviceInfo,
2675 &DeviceInfoData);
2676 if (!ok)
2677 {
2678 if (GetLastError() != ERROR_DI_DO_DEFAULT)
2679 SetErrBreak (("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (0x%08X)",
2680 GetLastError()));
2681 /* that's fine */
2682 }
2683
2684 /* install the files first */
2685 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES, hDeviceInfo,
2686 &DeviceInfoData);
2687 if (!ok)
2688 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (0x%08X)",
2689 GetLastError()));
2690
2691 /* get the device install parameters and disable filecopy */
2692 DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
2693 ok = SetupDiGetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2694 &DeviceInstallParams);
2695 if (ok)
2696 {
2697 DeviceInstallParams.Flags |= DI_NOFILECOPY;
2698 ok = SetupDiSetDeviceInstallParams (hDeviceInfo, &DeviceInfoData,
2699 &DeviceInstallParams);
2700 if (!ok)
2701 SetErrBreak (("SetupDiSetDeviceInstallParams failed (0x%08X)",
2702 GetLastError()));
2703 }
2704
2705 /*
2706 * Register any device-specific co-installers for this device,
2707 */
2708
2709 ok = SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS,
2710 hDeviceInfo,
2711 &DeviceInfoData);
2712 if (!ok)
2713 SetErrBreak (("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (0x%08X)",
2714 GetLastError()));
2715
2716 /*
2717 * install any installer-specified interfaces.
2718 * and then do the real install
2719 */
2720 ok = SetupDiCallClassInstaller (DIF_INSTALLINTERFACES,
2721 hDeviceInfo,
2722 &DeviceInfoData);
2723 if (!ok)
2724 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (0x%08X)",
2725 GetLastError()));
2726
2727 ok = SetupDiCallClassInstaller (DIF_INSTALLDEVICE,
2728 hDeviceInfo,
2729 &DeviceInfoData);
2730 if (!ok)
2731 SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (0x%08X)",
2732 GetLastError()));
2733
2734 /* Figure out NetCfgInstanceId */
2735 hkey = SetupDiOpenDevRegKey (hDeviceInfo,
2736 &DeviceInfoData,
2737 DICS_FLAG_GLOBAL,
2738 0,
2739 DIREG_DRV,
2740 KEY_READ);
2741 if (hkey == INVALID_HANDLE_VALUE)
2742 SetErrBreak (("SetupDiOpenDevRegKey failed (0x%08X)",
2743 GetLastError()));
2744
2745 cbSize = sizeof (pCfgGuidString);
2746 DWORD ret;
2747 ret = RegQueryValueEx (hkey, _T ("NetCfgInstanceId"), NULL,
2748 &dwValueType, (LPBYTE) pCfgGuidString, &cbSize);
2749 RegCloseKey (hkey);
2750
2751 ret = RenameConnection (pCfgGuidString, Bstr (aName));
2752 if (FAILED (ret))
2753 SetErrBreak (("Failed to set interface name (ret=0x%08X, "
2754 "pCfgGuidString='%ls', cbSize=%d)",
2755 ret, pCfgGuidString, cbSize));
2756 }
2757 while (0);
2758
2759 /*
2760 * cleanup
2761 */
2762
2763 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2764 {
2765 /* an error has occured, but the device is registered, we must remove it */
2766 if (ret != 0 && registered)
2767 SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2768
2769 found = SetupDiDeleteDeviceInfo (hDeviceInfo, &DeviceInfoData);
2770
2771 /* destroy the driver info list */
2772 if (destroyList)
2773 SetupDiDestroyDriverInfoList (hDeviceInfo, &DeviceInfoData,
2774 SPDIT_CLASSDRIVER);
2775 /* clean up the device info set */
2776 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2777 }
2778
2779 /* return the network connection GUID on success */
2780 if (VBOX_SUCCESS (vrc))
2781 {
2782 /* remove the curly bracket at the end */
2783 pCfgGuidString [_tcslen (pCfgGuidString) - 1] = '\0';
2784 LogFlowFunc (("Network connection GUID string = {%ls}\n", pCfgGuidString + 1));
2785
2786 aGUID = Guid (Utf8Str (pCfgGuidString + 1));
2787 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2788 Assert (!aGUID.isEmpty());
2789 }
2790
2791 LogFlowFunc (("vrc=%Vrc\n", vrc));
2792 LogFlowFuncLeave();
2793 return vrc;
2794}
2795
2796/* static */
2797int Host::removeNetworkInterface (SVCHlpClient *aClient,
2798 const Guid &aGUID,
2799 Utf8Str &aErrMsg)
2800{
2801 LogFlowFuncEnter();
2802 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", aGUID.raw()));
2803
2804 AssertReturn (aClient, VERR_INVALID_POINTER);
2805 AssertReturn (!aGUID.isEmpty(), VERR_INVALID_PARAMETER);
2806
2807 int vrc = VINF_SUCCESS;
2808
2809 do
2810 {
2811 TCHAR lszPnPInstanceId [512] = {0};
2812
2813 /* We have to find the device instance ID through a registry search */
2814
2815 HKEY hkeyNetwork = 0;
2816 HKEY hkeyConnection = 0;
2817
2818 do
2819 {
2820 char strRegLocation [256];
2821 sprintf (strRegLocation,
2822 "SYSTEM\\CurrentControlSet\\Control\\Network\\"
2823 "{4D36E972-E325-11CE-BFC1-08002BE10318}\\{%s}",
2824 aGUID.toString().raw());
2825 LONG status;
2826 status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, strRegLocation, 0,
2827 KEY_READ, &hkeyNetwork);
2828 if ((status != ERROR_SUCCESS) || !hkeyNetwork)
2829 SetErrBreak ((
2830 tr ("Host interface network is not found in registry (%s) [1]"),
2831 strRegLocation));
2832
2833 status = RegOpenKeyExA (hkeyNetwork, "Connection", 0,
2834 KEY_READ, &hkeyConnection);
2835 if ((status != ERROR_SUCCESS) || !hkeyConnection)
2836 SetErrBreak ((
2837 tr ("Host interface network is not found in registry (%s) [2]"),
2838 strRegLocation));
2839
2840 DWORD len = sizeof (lszPnPInstanceId);
2841 DWORD dwKeyType;
2842 status = RegQueryValueExW (hkeyConnection, L"PnPInstanceID", NULL,
2843 &dwKeyType, (LPBYTE) lszPnPInstanceId, &len);
2844 if ((status != ERROR_SUCCESS) || (dwKeyType != REG_SZ))
2845 SetErrBreak ((
2846 tr ("Host interface network is not found in registry (%s) [3]"),
2847 strRegLocation));
2848 }
2849 while (0);
2850
2851 if (hkeyConnection)
2852 RegCloseKey (hkeyConnection);
2853 if (hkeyNetwork)
2854 RegCloseKey (hkeyNetwork);
2855
2856 if (VBOX_FAILURE (vrc))
2857 break;
2858
2859 /*
2860 * Now we are going to enumerate all network devices and
2861 * wait until we encounter the right device instance ID
2862 */
2863
2864 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2865
2866 do
2867 {
2868 BOOL ok;
2869 DWORD ret = 0;
2870 GUID netGuid;
2871 SP_DEVINFO_DATA DeviceInfoData;
2872 DWORD index = 0;
2873 BOOL found = FALSE;
2874 DWORD size = 0;
2875
2876 /* initialize the structure size */
2877 DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
2878
2879 /* copy the net class GUID */
2880 memcpy (&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2881
2882 /* return a device info set contains all installed devices of the Net class */
2883 hDeviceInfo = SetupDiGetClassDevs (&netGuid, NULL, NULL, DIGCF_PRESENT);
2884
2885 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2886 SetErrBreak (("SetupDiGetClassDevs failed (0x%08X)", GetLastError()));
2887
2888 /* enumerate the driver info list */
2889 while (TRUE)
2890 {
2891 TCHAR *deviceHwid;
2892
2893 ok = SetupDiEnumDeviceInfo (hDeviceInfo, index, &DeviceInfoData);
2894
2895 if (!ok)
2896 {
2897 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2898 break;
2899 else
2900 {
2901 index++;
2902 continue;
2903 }
2904 }
2905
2906 /* try to get the hardware ID registry property */
2907 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2908 &DeviceInfoData,
2909 SPDRP_HARDWAREID,
2910 NULL,
2911 NULL,
2912 0,
2913 &size);
2914 if (!ok)
2915 {
2916 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2917 {
2918 index++;
2919 continue;
2920 }
2921
2922 deviceHwid = (TCHAR *) malloc (size);
2923 ok = SetupDiGetDeviceRegistryProperty (hDeviceInfo,
2924 &DeviceInfoData,
2925 SPDRP_HARDWAREID,
2926 NULL,
2927 (PBYTE)deviceHwid,
2928 size,
2929 NULL);
2930 if (!ok)
2931 {
2932 free (deviceHwid);
2933 deviceHwid = NULL;
2934 index++;
2935 continue;
2936 }
2937 }
2938 else
2939 {
2940 /* something is wrong. This shouldn't have worked with a NULL buffer */
2941 index++;
2942 continue;
2943 }
2944
2945 for (TCHAR *t = deviceHwid;
2946 t && *t && t < &deviceHwid[size / sizeof(TCHAR)];
2947 t += _tcslen (t) + 1)
2948 {
2949 if (!_tcsicmp (DRIVERHWID, t))
2950 {
2951 /* get the device instance ID */
2952 TCHAR devID [MAX_DEVICE_ID_LEN];
2953 if (CM_Get_Device_ID(DeviceInfoData.DevInst,
2954 devID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2955 {
2956 /* compare to what we determined before */
2957 if (wcscmp(devID, lszPnPInstanceId) == 0)
2958 {
2959 found = TRUE;
2960 break;
2961 }
2962 }
2963 }
2964 }
2965
2966 if (deviceHwid)
2967 {
2968 free (deviceHwid);
2969 deviceHwid = NULL;
2970 }
2971
2972 if (found)
2973 break;
2974
2975 index++;
2976 }
2977
2978 if (found == FALSE)
2979 SetErrBreak ((tr ("Host Interface Network driver not found (0x%08X)"),
2980 GetLastError()));
2981
2982 ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData);
2983 if (!ok)
2984 SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)",
2985 GetLastError()));
2986
2987 ok = SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2988 if (!ok)
2989 SetErrBreak (("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)",
2990 GetLastError()));
2991 }
2992 while (0);
2993
2994 /* clean up the device info set */
2995 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2996 SetupDiDestroyDeviceInfoList (hDeviceInfo);
2997
2998 if (VBOX_FAILURE (vrc))
2999 break;
3000 }
3001 while (0);
3002
3003 LogFlowFunc (("vrc=%Vrc\n", vrc));
3004 LogFlowFuncLeave();
3005 return vrc;
3006}
3007
3008#undef SetErrBreak
3009
3010/* static */
3011HRESULT Host::networkInterfaceHelperClient (SVCHlpClient *aClient,
3012 Progress *aProgress,
3013 void *aUser, int *aVrc)
3014{
3015 LogFlowFuncEnter();
3016 LogFlowFunc (("aClient={%p}, aProgress={%p}, aUser={%p}\n",
3017 aClient, aProgress, aUser));
3018
3019 AssertReturn ((aClient == NULL && aProgress == NULL && aVrc == NULL) ||
3020 (aClient != NULL && aProgress != NULL && aVrc != NULL),
3021 E_POINTER);
3022 AssertReturn (aUser, E_POINTER);
3023
3024 std::auto_ptr <NetworkInterfaceHelperClientData>
3025 d (static_cast <NetworkInterfaceHelperClientData *> (aUser));
3026
3027 if (aClient == NULL)
3028 {
3029 /* "cleanup only" mode, just return (it will free aUser) */
3030 return S_OK;
3031 }
3032
3033 HRESULT rc = S_OK;
3034 int vrc = VINF_SUCCESS;
3035
3036 switch (d->msgCode)
3037 {
3038 case SVCHlpMsg::CreateHostNetworkInterface:
3039 {
3040 LogFlowFunc (("CreateHostNetworkInterface:\n"));
3041 LogFlowFunc (("Network connection name = '%ls'\n", d->name.raw()));
3042
3043 /* write message and parameters */
3044 vrc = aClient->write (d->msgCode);
3045 if (VBOX_FAILURE (vrc)) break;
3046 vrc = aClient->write (Utf8Str (d->name));
3047 if (VBOX_FAILURE (vrc)) break;
3048
3049 /* wait for a reply */
3050 bool endLoop = false;
3051 while (!endLoop)
3052 {
3053 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
3054
3055 vrc = aClient->read (reply);
3056 if (VBOX_FAILURE (vrc)) break;
3057
3058 switch (reply)
3059 {
3060 case SVCHlpMsg::CreateHostNetworkInterface_OK:
3061 {
3062 /* read the GUID */
3063 Guid guid;
3064 vrc = aClient->read (guid);
3065 if (VBOX_FAILURE (vrc)) break;
3066
3067 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", guid.raw()));
3068
3069 /* initialize the object returned to the caller by
3070 * CreateHostNetworkInterface() */
3071 rc = d->iface->init (d->name, guid);
3072 endLoop = true;
3073 break;
3074 }
3075 case SVCHlpMsg::Error:
3076 {
3077 /* read the error message */
3078 Utf8Str errMsg;
3079 vrc = aClient->read (errMsg);
3080 if (VBOX_FAILURE (vrc)) break;
3081
3082 rc = setError (E_FAIL, errMsg);
3083 endLoop = true;
3084 break;
3085 }
3086 default:
3087 {
3088 endLoop = true;
3089 ComAssertMsgFailedBreak ((
3090 "Invalid message code %d (%08lX)\n",
3091 reply, reply),
3092 rc = E_FAIL);
3093 }
3094 }
3095 }
3096
3097 break;
3098 }
3099 case SVCHlpMsg::RemoveHostNetworkInterface:
3100 {
3101 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
3102 LogFlowFunc (("Network connection GUID = {%Vuuid}\n", d->guid.raw()));
3103
3104 /* write message and parameters */
3105 vrc = aClient->write (d->msgCode);
3106 if (VBOX_FAILURE (vrc)) break;
3107 vrc = aClient->write (d->guid);
3108 if (VBOX_FAILURE (vrc)) break;
3109
3110 /* wait for a reply */
3111 bool endLoop = false;
3112 while (!endLoop)
3113 {
3114 SVCHlpMsg::Code reply = SVCHlpMsg::Null;
3115
3116 vrc = aClient->read (reply);
3117 if (VBOX_FAILURE (vrc)) break;
3118
3119 switch (reply)
3120 {
3121 case SVCHlpMsg::OK:
3122 {
3123 /* no parameters */
3124 rc = S_OK;
3125 endLoop = true;
3126 break;
3127 }
3128 case SVCHlpMsg::Error:
3129 {
3130 /* read the error message */
3131 Utf8Str errMsg;
3132 vrc = aClient->read (errMsg);
3133 if (VBOX_FAILURE (vrc)) break;
3134
3135 rc = setError (E_FAIL, errMsg);
3136 endLoop = true;
3137 break;
3138 }
3139 default:
3140 {
3141 endLoop = true;
3142 ComAssertMsgFailedBreak ((
3143 "Invalid message code %d (%08lX)\n",
3144 reply, reply),
3145 rc = E_FAIL);
3146 }
3147 }
3148 }
3149
3150 break;
3151 }
3152 default:
3153 ComAssertMsgFailedBreak ((
3154 "Invalid message code %d (%08lX)\n",
3155 d->msgCode, d->msgCode),
3156 rc = E_FAIL);
3157 }
3158
3159 if (aVrc)
3160 *aVrc = vrc;
3161
3162 LogFlowFunc (("rc=0x%08X, vrc=%Vrc\n", rc, vrc));
3163 LogFlowFuncLeave();
3164 return rc;
3165}
3166
3167/* static */
3168int Host::networkInterfaceHelperServer (SVCHlpClient *aClient,
3169 SVCHlpMsg::Code aMsgCode)
3170{
3171 LogFlowFuncEnter();
3172 LogFlowFunc (("aClient={%p}, aMsgCode=%d\n", aClient, aMsgCode));
3173
3174 AssertReturn (aClient, VERR_INVALID_POINTER);
3175
3176 int vrc = VINF_SUCCESS;
3177
3178 switch (aMsgCode)
3179 {
3180 case SVCHlpMsg::CreateHostNetworkInterface:
3181 {
3182 LogFlowFunc (("CreateHostNetworkInterface:\n"));
3183
3184 Utf8Str name;
3185 vrc = aClient->read (name);
3186 if (VBOX_FAILURE (vrc)) break;
3187
3188 Guid guid;
3189 Utf8Str errMsg;
3190 vrc = createNetworkInterface (aClient, name, guid, errMsg);
3191
3192 if (VBOX_SUCCESS (vrc))
3193 {
3194 /* write success followed by GUID */
3195 vrc = aClient->write (SVCHlpMsg::CreateHostNetworkInterface_OK);
3196 if (VBOX_FAILURE (vrc)) break;
3197 vrc = aClient->write (guid);
3198 if (VBOX_FAILURE (vrc)) break;
3199 }
3200 else
3201 {
3202 /* write failure followed by error message */
3203 if (errMsg.isEmpty())
3204 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
3205 vrc = aClient->write (SVCHlpMsg::Error);
3206 if (VBOX_FAILURE (vrc)) break;
3207 vrc = aClient->write (errMsg);
3208 if (VBOX_FAILURE (vrc)) break;
3209 }
3210
3211 break;
3212 }
3213 case SVCHlpMsg::RemoveHostNetworkInterface:
3214 {
3215 LogFlowFunc (("RemoveHostNetworkInterface:\n"));
3216
3217 Guid guid;
3218 vrc = aClient->read (guid);
3219 if (VBOX_FAILURE (vrc)) break;
3220
3221 Utf8Str errMsg;
3222 vrc = removeNetworkInterface (aClient, guid, errMsg);
3223
3224 if (VBOX_SUCCESS (vrc))
3225 {
3226 /* write parameter-less success */
3227 vrc = aClient->write (SVCHlpMsg::OK);
3228 if (VBOX_FAILURE (vrc)) break;
3229 }
3230 else
3231 {
3232 /* write failure followed by error message */
3233 if (errMsg.isEmpty())
3234 errMsg = Utf8StrFmt ("Unspecified error (%Vrc)", vrc);
3235 vrc = aClient->write (SVCHlpMsg::Error);
3236 if (VBOX_FAILURE (vrc)) break;
3237 vrc = aClient->write (errMsg);
3238 if (VBOX_FAILURE (vrc)) break;
3239 }
3240
3241 break;
3242 }
3243 default:
3244 AssertMsgFailedBreak ((
3245 "Invalid message code %d (%08lX)\n", aMsgCode, aMsgCode),
3246 VERR_GENERAL_FAILURE);
3247 }
3248
3249 LogFlowFunc (("vrc=%Vrc\n", vrc));
3250 LogFlowFuncLeave();
3251 return vrc;
3252}
3253
3254#endif /* RT_OS_WINDOWS */
3255
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