VirtualBox

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

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

Main: Renamed AutoLock => AutoWriteLock; AutoReaderLock => AutoReadLock.

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